summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/os/MessageQueue.java76
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java50
-rw-r--r--core/java/android/view/IWindowSession.aidl4
-rw-r--r--core/java/android/view/InputChannel.aidl20
-rw-r--r--core/java/android/view/InputChannel.java152
-rw-r--r--core/java/android/view/InputHandler.java53
-rw-r--r--core/java/android/view/InputQueue.java126
-rw-r--r--core/java/android/view/InputTarget.java57
-rwxr-xr-xcore/java/android/view/KeyEvent.java23
-rw-r--r--core/java/android/view/MotionEvent.java51
-rw-r--r--core/java/android/view/SurfaceView.java2
-rw-r--r--core/java/android/view/ViewRoot.java237
-rw-r--r--core/java/android/view/WindowManagerPolicy.java8
-rw-r--r--core/java/com/android/internal/view/BaseInputHandler.java39
-rw-r--r--core/jni/Android.mk5
-rw-r--r--core/jni/AndroidRuntime.cpp10
-rw-r--r--core/jni/android_os_MessageQueue.cpp348
-rw-r--r--core/jni/android_os_MessageQueue.h30
-rw-r--r--core/jni/android_view_InputChannel.cpp288
-rw-r--r--core/jni/android_view_InputChannel.h40
-rw-r--r--core/jni/android_view_InputQueue.cpp471
-rw-r--r--core/jni/android_view_InputTarget.cpp97
-rw-r--r--core/jni/android_view_InputTarget.h31
-rw-r--r--core/jni/android_view_KeyEvent.cpp124
-rw-r--r--core/jni/android_view_KeyEvent.h35
-rw-r--r--core/jni/android_view_MotionEvent.cpp310
-rw-r--r--core/jni/android_view_MotionEvent.h38
-rw-r--r--core/jni/android_view_ViewRoot.cpp36
28 files changed, 2319 insertions, 442 deletions
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index d394a46..df30c76 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -16,13 +16,11 @@
package android.os;
-import java.util.ArrayList;
-
import android.util.AndroidRuntimeException;
import android.util.Config;
import android.util.Log;
-import com.android.internal.os.RuntimeInit;
+import java.util.ArrayList;
/**
* Low-level class holding the list of messages to be dispatched by a
@@ -34,11 +32,18 @@ import com.android.internal.os.RuntimeInit;
*/
public class MessageQueue {
Message mMessages;
- private final ArrayList mIdleHandlers = new ArrayList();
+ private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private boolean mQuiting = false;
- private int mObject = 0; // used by native code
boolean mQuitAllowed = true;
+ @SuppressWarnings("unused")
+ private int mPtr; // used by native code
+
+ private native void nativeInit();
+ private native void nativeDestroy();
+ private native boolean nativePollOnce(int timeoutMillis);
+ private native void nativeWake();
+
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
@@ -85,55 +90,39 @@ public class MessageQueue {
mIdleHandlers.remove(handler);
}
}
-
- // Add an input pipe to the set being selected over. If token is
- // negative, remove 'handler's entry from the current set and forget
- // about it.
- void setInputToken(int token, int region, Handler handler) {
- if (token >= 0) nativeRegisterInputStream(token, region, handler);
- else nativeUnregisterInputStream(token);
- }
-
+
MessageQueue() {
nativeInit();
}
- private native void nativeInit();
-
- /**
- * @param token fd of the readable end of the input stream
- * @param region fd of the ashmem region used for data transport alongside the 'token' fd
- * @param handler Handler from which to make input messages based on data read from the fd
- */
- private native void nativeRegisterInputStream(int token, int region, Handler handler);
- private native void nativeUnregisterInputStream(int token);
- private native void nativeSignal();
-
- /**
- * Wait until the designated time for new messages to arrive.
- *
- * @param when Timestamp in SystemClock.uptimeMillis() base of the next message in the queue.
- * If 'when' is zero, the method will check for incoming messages without blocking. If
- * 'when' is negative, the method will block forever waiting for the next message.
- * @return
- */
- private native int nativeWaitForNext(long when);
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nativeDestroy();
+ } finally {
+ super.finalize();
+ }
+ }
final Message next() {
boolean tryIdle = true;
// when we start out, we'll just touch the input pipes and then go from there
- long timeToNextEventMillis = 0;
+ int timeToNextEventMillis = 0;
while (true) {
long now;
Object[] idlers = null;
- nativeWaitForNext(timeToNextEventMillis);
+ boolean dispatched = nativePollOnce(timeToNextEventMillis);
// Try to retrieve the next message, returning if found.
synchronized (this) {
now = SystemClock.uptimeMillis();
Message msg = pullNextLocked(now);
- if (msg != null) return msg;
+ if (msg != null) {
+ return msg;
+ }
+
if (tryIdle && mIdleHandlers.size() > 0) {
idlers = mIdleHandlers.toArray();
}
@@ -170,9 +159,14 @@ public class MessageQueue {
synchronized (this) {
// No messages, nobody to tell about it... time to wait!
if (mMessages != null) {
- if (mMessages.when - now > 0) {
+ long longTimeToNextEventMillis = mMessages.when - now;
+
+ if (longTimeToNextEventMillis > 0) {
Binder.flushPendingCommands();
- timeToNextEventMillis = mMessages.when - now;
+ timeToNextEventMillis = (int) Math.min(longTimeToNextEventMillis,
+ Integer.MAX_VALUE);
+ } else {
+ timeToNextEventMillis = 0;
}
} else {
Binder.flushPendingCommands();
@@ -230,7 +224,7 @@ public class MessageQueue {
msg.next = prev.next;
prev.next = msg;
}
- nativeSignal();
+ nativeWake();
}
return true;
}
@@ -351,7 +345,7 @@ public class MessageQueue {
void poke()
{
synchronized (this) {
- nativeSignal();
+ nativeWake();
}
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 2ade44e..3f5d6ca 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -18,6 +18,7 @@ package android.service.wallpaper;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
+import com.android.internal.view.BaseInputHandler;
import com.android.internal.view.BaseSurfaceHolder;
import android.annotation.SdkConstant;
@@ -39,6 +40,10 @@ import android.util.Log;
import android.util.LogPrinter;
import android.view.Gravity;
import android.view.IWindowSession;
+import android.view.InputChannel;
+import android.view.InputHandler;
+import android.view.InputQueue;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
@@ -46,6 +51,7 @@ import android.view.ViewGroup;
import android.view.ViewRoot;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
+import android.view.WindowManagerPolicy;
import java.util.ArrayList;
@@ -146,6 +152,7 @@ public abstract class WallpaperService extends Service {
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
IWindowSession mSession;
+ InputChannel mInputChannel;
final Object mLock = new Object();
boolean mOffsetMessageEnqueued;
@@ -205,6 +212,30 @@ public abstract class WallpaperService extends Service {
};
+ final InputHandler mInputHandler = new BaseInputHandler() {
+ @Override
+ public void handleTouch(MotionEvent event, Runnable finishedCallback) {
+ try {
+ synchronized (mLock) {
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ if (mPendingMove != null) {
+ mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
+ mPendingMove.recycle();
+ }
+ mPendingMove = event;
+ } else {
+ mPendingMove = null;
+ }
+ Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT,
+ event);
+ mCaller.sendMessage(msg);
+ }
+ } finally {
+ finishedCallback.run();
+ }
+ }
+ };
+
final BaseIWindow mWindow = new BaseIWindow() {
@Override
public boolean onDispatchPointer(MotionEvent event, long eventTime,
@@ -487,8 +518,15 @@ public abstract class WallpaperService extends Service {
mLayout.setTitle(WallpaperService.this.getClass().getName());
mLayout.windowAnimations =
com.android.internal.R.style.Animation_Wallpaper;
- mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
+ mInputChannel = new InputChannel();
+ mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets,
+ mInputChannel);
mCreated = true;
+
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ InputQueue.registerInputChannel(mInputChannel, mInputHandler,
+ Looper.myQueue());
+ }
}
mSurfaceHolder.mSurfaceLock.lock();
@@ -587,6 +625,7 @@ public abstract class WallpaperService extends Service {
mSurfaceHolder.setSizeFromLayout();
mInitializing = true;
mSession = ViewRoot.getWindowSession(getMainLooper());
+
mWindow.setSession(mSession);
IntentFilter filter = new IntentFilter();
@@ -730,6 +769,15 @@ public abstract class WallpaperService extends Service {
try {
if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
+ mSurfaceHolder.getSurface() + " of: " + this);
+
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ if (mInputChannel != null) {
+ InputQueue.unregisterInputChannel(mInputChannel);
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
+
mSession.remove(mWindow);
} catch (RemoteException e) {
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 01f07d6..4647fb4 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -21,6 +21,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
+import android.view.InputChannel;
import android.view.IWindow;
import android.view.MotionEvent;
import android.view.WindowManager;
@@ -33,6 +34,9 @@ import android.view.Surface;
*/
interface IWindowSession {
int add(IWindow window, in WindowManager.LayoutParams attrs,
+ in int viewVisibility, out Rect outContentInsets,
+ out InputChannel outInputChannel);
+ int addWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, out Rect outContentInsets);
void remove(IWindow window);
diff --git a/core/java/android/view/InputChannel.aidl b/core/java/android/view/InputChannel.aidl
new file mode 100644
index 0000000..74c0aa4
--- /dev/null
+++ b/core/java/android/view/InputChannel.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/InputChannel.aidl
+**
+** Copyright 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;
+
+parcelable InputChannel;
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
new file mode 100644
index 0000000..66a83b8
--- /dev/null
+++ b/core/java/android/view/InputChannel.java
@@ -0,0 +1,152 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Slog;
+
+/**
+ * An input channel specifies the file descriptors used to send input events to
+ * a window in another process. It is Parcelable so that it can be transmitted
+ * to the ViewRoot through a Binder transaction as part of registering the Window.
+ * @hide
+ */
+public class InputChannel implements Parcelable {
+ private static final String TAG = "InputChannel";
+
+ public static final Parcelable.Creator<InputChannel> CREATOR
+ = new Parcelable.Creator<InputChannel>() {
+ public InputChannel createFromParcel(Parcel source) {
+ InputChannel result = new InputChannel();
+ result.readFromParcel(source);
+ return result;
+ }
+
+ public InputChannel[] newArray(int size) {
+ return new InputChannel[size];
+ }
+ };
+
+ @SuppressWarnings("unused")
+ private int mPtr; // used by native code
+
+ private boolean mDisposeAfterWriteToParcel;
+
+ private static native InputChannel[] nativeOpenInputChannelPair(String name);
+
+ private native void nativeDispose(boolean finalized);
+ private native void nativeTransferTo(InputChannel other);
+ private native void nativeReadFromParcel(Parcel parcel);
+ private native void nativeWriteToParcel(Parcel parcel);
+
+ private native String nativeGetName();
+
+ /**
+ * Creates an uninitialized input channel.
+ * It can be initialized by reading from a Parcel or by transferring the state of
+ * another input channel into this one.
+ */
+ public InputChannel() {
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nativeDispose(true);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Creates a new input channel pair. One channel should be provided to the input
+ * dispatcher and the other to the application's input queue.
+ * @param name The descriptive (non-unique) name of the channel pair.
+ * @return A pair of input channels. They are symmetric and indistinguishable.
+ */
+ public static InputChannel[] openInputChannelPair(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("name must not be null");
+ }
+
+ Slog.d(TAG, "Opening input channel pair '" + name + "'");
+ return nativeOpenInputChannelPair(name);
+ }
+
+ /**
+ * Gets the name of the input channel.
+ * @return The input channel name.
+ */
+ public String getName() {
+ String name = nativeGetName();
+ return name != null ? name : "uninitialized";
+ }
+
+ /**
+ * Disposes the input channel.
+ * Explicitly releases the reference this object is holding on the input channel.
+ * When all references are released, the input channel will be closed.
+ */
+ public void dispose() {
+ nativeDispose(false);
+ }
+
+ /**
+ * Transfers ownership of the internal state of the input channel to another
+ * instance and invalidates this instance. This is used to pass an input channel
+ * as an out parameter in a binder call.
+ * @param other The other input channel instance.
+ */
+ public void transferToBinderOutParameter(InputChannel outParameter) {
+ if (outParameter == null) {
+ throw new IllegalArgumentException("outParameter must not be null");
+ }
+
+ nativeTransferTo(outParameter);
+ outParameter.mDisposeAfterWriteToParcel = true;
+ }
+
+ public int describeContents() {
+ return Parcelable.CONTENTS_FILE_DESCRIPTOR;
+ }
+
+ public void readFromParcel(Parcel in) {
+ if (in == null) {
+ throw new IllegalArgumentException("in must not be null");
+ }
+
+ nativeReadFromParcel(in);
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ if (out == null) {
+ throw new IllegalArgumentException("out must not be null");
+ }
+
+ nativeWriteToParcel(out);
+
+ if (mDisposeAfterWriteToParcel) {
+ dispose();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/core/java/android/view/InputHandler.java b/core/java/android/view/InputHandler.java
new file mode 100644
index 0000000..816f622
--- /dev/null
+++ b/core/java/android/view/InputHandler.java
@@ -0,0 +1,53 @@
+/*
+ * 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 interface InputHandler {
+ /**
+ * Handle a key 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 key event data.
+ * @param finishedCallback The callback to invoke when event processing is finished.
+ */
+ public void handleKey(KeyEvent event, Runnable finishedCallback);
+
+ /**
+ * Handle a touch 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 motion event data.
+ * @param finishedCallback The callback to invoke when event processing is finished.
+ */
+ public void handleTouch(MotionEvent event, Runnable finishedCallback);
+
+ /**
+ * Handle a trackball 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 motion event data.
+ * @param finishedCallback The callback to invoke when event processing is finished.
+ */
+ public void handleTrackball(MotionEvent event, Runnable finishedCallback);
+}
diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java
new file mode 100644
index 0000000..b38f7d5
--- /dev/null
+++ b/core/java/android/view/InputQueue.java
@@ -0,0 +1,126 @@
+/*
+ * 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;
+
+import android.os.MessageQueue;
+import android.util.Slog;
+
+/**
+ * An input queue provides a mechanism for an application to receive incoming
+ * input events sent over an input channel. Signalling is implemented by MessageQueue.
+ * @hide
+ */
+public final class InputQueue {
+ private static final String TAG = "InputQueue";
+
+ // Describes the interpretation of an event.
+ // XXX This concept is tentative. See comments in android/input.h.
+ public static final int INPUT_EVENT_NATURE_KEY = 1;
+ public static final int INPUT_EVENT_NATURE_TOUCH = 2;
+ public static final int INPUT_EVENT_NATURE_TRACKBALL = 3;
+
+ private static 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);
+
+ private InputQueue() {
+ }
+
+ /**
+ * 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.
+ */
+ 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) {
+ 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.
+ */
+ public static void unregisterInputChannel(InputChannel inputChannel) {
+ if (inputChannel == null) {
+ throw new IllegalArgumentException("inputChannel must not be null");
+ }
+
+ synchronized (sLock) {
+ Slog.d(TAG, "Unregistering input channel '" + inputChannel + "'");
+ nativeUnregisterInputChannel(inputChannel);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void dispatchKeyEvent(InputHandler inputHandler,
+ KeyEvent event, int nature, long finishedToken) {
+ Runnable finishedCallback = new FinishedCallback(finishedToken);
+
+ if (nature == INPUT_EVENT_NATURE_KEY) {
+ inputHandler.handleKey(event, finishedCallback);
+ } else {
+ Slog.d(TAG, "Unsupported nature for key event: " + nature);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void dispatchMotionEvent(InputHandler inputHandler,
+ MotionEvent event, int nature, long finishedToken) {
+ Runnable finishedCallback = new FinishedCallback(finishedToken);
+
+ if (nature == INPUT_EVENT_NATURE_TOUCH) {
+ inputHandler.handleTouch(event, finishedCallback);
+ } else if (nature == INPUT_EVENT_NATURE_TRACKBALL) {
+ inputHandler.handleTrackball(event, finishedCallback);
+ } else {
+ Slog.d(TAG, "Unsupported nature for motion event: " + nature);
+ }
+ }
+
+ // TODO consider recycling finished callbacks when done
+ private static class FinishedCallback implements Runnable {
+ private long mFinishedToken;
+
+ public FinishedCallback(long finishedToken) {
+ mFinishedToken = finishedToken;
+ }
+
+ public void run() {
+ synchronized (sLock) {
+ nativeFinished(mFinishedToken);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/InputTarget.java b/core/java/android/view/InputTarget.java
new file mode 100644
index 0000000..e56e03c
--- /dev/null
+++ b/core/java/android/view/InputTarget.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+/**
+ * An input target specifies how an input event is to be dispatched to a particular window
+ * including the window's input channel, control flags, a timeout, and an X / Y offset to
+ * be added to input event coordinates to compensate for the absolute position of the
+ * window area.
+ *
+ * These parameters are used by the native input dispatching code.
+ * @hide
+ */
+public class InputTarget {
+ public InputChannel mInputChannel;
+ public int mFlags;
+ public long mTimeoutNanos;
+ public float mXOffset;
+ public float mYOffset;
+
+ /**
+ * This flag indicates that subsequent event delivery should be held until the
+ * current event is delivered to this target or a timeout occurs.
+ */
+ public static int FLAG_SYNC = 0x01;
+
+ /**
+ * This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
+ * this target and so should instead be delivered as an ACTION_OUTSIDE to this target.
+ */
+ public static int FLAG_OUTSIDE = 0x02;
+
+ /*
+ * This flag indicates that a KeyEvent or MotionEvent is being canceled.
+ * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
+ * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL.
+ */
+ public static int FLAG_CANCEL = 0x04;
+
+ public void recycle() {
+ mInputChannel = null;
+ }
+}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 9aa16b5..ae9746e 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -127,6 +127,7 @@ public class KeyEvent implements Parcelable {
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
+ // native/include/android/keycodes.h
// frameworks/base/include/ui/KeycodeLabels.h
// tools/puppet_master/PuppetMaster/nav_keys.py
// frameworks/base/core/res/res/values/attrs.xml
@@ -162,7 +163,7 @@ public class KeyEvent implements Parcelable {
* key code is not {#link {@link #KEYCODE_UNKNOWN} then the
* {#link {@link #getRepeatCount()} method returns the number of times
* the given key code should be executed.
- * Otherwise, if the key code {@link #KEYCODE_UNKNOWN}, then
+ * Otherwise, if the key code is {@link #KEYCODE_UNKNOWN}, then
* this is a sequence of characters as returned by {@link #getCharacters}.
*/
public static final int ACTION_MULTIPLE = 2;
@@ -330,7 +331,7 @@ public class KeyEvent implements Parcelable {
private int mMetaState;
private int mAction;
private int mKeyCode;
- private int mScancode;
+ private int mScanCode;
private int mRepeatCount;
private int mDeviceId;
private int mFlags;
@@ -480,7 +481,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = repeat;
mMetaState = metaState;
mDeviceId = device;
- mScancode = scancode;
+ mScanCode = scancode;
}
/**
@@ -510,7 +511,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = repeat;
mMetaState = metaState;
mDeviceId = device;
- mScancode = scancode;
+ mScanCode = scancode;
mFlags = flags;
}
@@ -547,7 +548,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = origEvent.mRepeatCount;
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
- mScancode = origEvent.mScancode;
+ mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
mCharacters = origEvent.mCharacters;
}
@@ -572,7 +573,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = newRepeat;
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
- mScancode = origEvent.mScancode;
+ mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
mCharacters = origEvent.mCharacters;
}
@@ -625,7 +626,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = origEvent.mRepeatCount;
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
- mScancode = origEvent.mScancode;
+ mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
// Don't copy mCharacters, since one way or the other we'll lose it
// when changing the action.
@@ -859,7 +860,7 @@ public class KeyEvent implements Parcelable {
* Mostly this is here for debugging purposes.
*/
public final int getScanCode() {
- return mScancode;
+ return mScanCode;
}
/**
@@ -1183,7 +1184,7 @@ public class KeyEvent implements Parcelable {
public String toString() {
return "KeyEvent{action=" + mAction + " code=" + mKeyCode
+ " repeat=" + mRepeatCount
- + " meta=" + mMetaState + " scancode=" + mScancode
+ + " meta=" + mMetaState + " scancode=" + mScanCode
+ " mFlags=" + mFlags + "}";
}
@@ -1208,7 +1209,7 @@ public class KeyEvent implements Parcelable {
out.writeInt(mRepeatCount);
out.writeInt(mMetaState);
out.writeInt(mDeviceId);
- out.writeInt(mScancode);
+ out.writeInt(mScanCode);
out.writeInt(mFlags);
out.writeLong(mDownTime);
out.writeLong(mEventTime);
@@ -1220,7 +1221,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = in.readInt();
mMetaState = in.readInt();
mDeviceId = in.readInt();
- mScancode = in.readInt();
+ mScanCode = in.readInt();
mFlags = in.readInt();
mDownTime = in.readLong();
mEventTime = in.readLong();
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index eefbf7a..1f06191 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -248,17 +248,17 @@ public final class MotionEvent implements Parcelable {
private RuntimeException mRecycledLocation;
private boolean mRecycled;
- private MotionEvent() {
- mPointerIdentifiers = new int[BASE_AVAIL_POINTERS];
- mDataSamples = new float[BASE_AVAIL_POINTERS*BASE_AVAIL_SAMPLES*NUM_SAMPLE_DATA];
- mTimeSamples = new long[BASE_AVAIL_SAMPLES];
+ private MotionEvent(int pointerCount, int sampleCount) {
+ mPointerIdentifiers = new int[pointerCount];
+ mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
+ mTimeSamples = new long[sampleCount];
}
static private MotionEvent obtain() {
final MotionEvent ev;
synchronized (gRecyclerLock) {
if (gRecyclerTop == null) {
- return new MotionEvent();
+ return new MotionEvent(BASE_AVAIL_POINTERS, BASE_AVAIL_SAMPLES);
}
ev = gRecyclerTop;
gRecyclerTop = ev.mNext;
@@ -269,6 +269,45 @@ public final class MotionEvent implements Parcelable {
ev.mNext = null;
return ev;
}
+
+ @SuppressWarnings("unused") // used by native code
+ static private MotionEvent obtain(int pointerCount, int sampleCount) {
+ final MotionEvent ev;
+ synchronized (gRecyclerLock) {
+ if (gRecyclerTop == null) {
+ if (pointerCount < BASE_AVAIL_POINTERS) {
+ pointerCount = BASE_AVAIL_POINTERS;
+ }
+ if (sampleCount < BASE_AVAIL_SAMPLES) {
+ sampleCount = BASE_AVAIL_SAMPLES;
+ }
+ return new MotionEvent(pointerCount, sampleCount);
+ }
+ ev = gRecyclerTop;
+ gRecyclerTop = ev.mNext;
+ gRecyclerUsed--;
+ }
+ ev.mRecycledLocation = null;
+ ev.mRecycled = false;
+ ev.mNext = null;
+
+ if (ev.mPointerIdentifiers.length < pointerCount) {
+ ev.mPointerIdentifiers = new int[pointerCount];
+ }
+
+ final int timeSamplesLength = ev.mTimeSamples.length;
+ if (timeSamplesLength < sampleCount) {
+ ev.mTimeSamples = new long[sampleCount];
+ }
+
+ final int dataSamplesLength = ev.mDataSamples.length;
+ final int neededDataSamplesLength = pointerCount * sampleCount * NUM_SAMPLE_DATA;
+ if (dataSamplesLength < neededDataSamplesLength) {
+ ev.mDataSamples = new float[neededDataSamplesLength];
+ }
+
+ return ev;
+ }
/**
* Create a new MotionEvent, filling in all of the basic values that
@@ -1022,7 +1061,7 @@ public final class MotionEvent implements Parcelable {
}
/**
- * Returns a bitfield indicating which edges, if any, where touched by this
+ * Returns a bitfield indicating which edges, if any, were touched by this
* MotionEvent. For touch events, clients can use this to determine if the
* user's finger was touching the edge of the display.
*
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0f0cf60..8beceec 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -471,7 +471,7 @@ public class SurfaceView extends View {
mWindow = new MyWindow(this);
mLayout.type = mWindowType;
mLayout.gravity = Gravity.LEFT|Gravity.TOP;
- mSession.add(mWindow, mLayout,
+ mSession.addWithoutInputChannel(mWindow, mLayout,
mVisible ? VISIBLE : GONE, mContentInsets);
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index aa124e6..a41c690 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -153,6 +153,7 @@ public final class ViewRoot extends Handler implements ViewParent,
CompatibilityInfo.Translator mTranslator;
final View.AttachInfo mAttachInfo;
+ InputChannel mInputChannel;
final Rect mTempRect; // used in the transaction to not thrash the heap.
final Rect mVisRect; // used to retrieve visible rect of focused view.
@@ -219,7 +220,7 @@ public final class ViewRoot extends Handler implements ViewParent,
AudioManager mAudioManager;
private final int mDensity;
-
+
public static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
@@ -434,9 +435,6 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
- // fd [0] is the receiver, [1] is the sender
- private native int[] makeInputChannel();
-
/**
* We have one child
*/
@@ -488,25 +486,20 @@ public final class ViewRoot extends Handler implements ViewParent,
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
- // Set up the input event channel
- if (false) {
- int[] fds = makeInputChannel();
- if (DEBUG_INPUT) {
- Log.v(TAG, "makeInputChannel() returned " + fds);
- }
- }
-
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
+ mInputChannel = new InputChannel();
try {
res = sWindowSession.add(mWindow, mWindowAttributes,
- getHostVisibility(), mAttachInfo.mContentInsets);
+ getHostVisibility(), mAttachInfo.mContentInsets,
+ mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
+ mInputChannel = null;
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
} finally {
@@ -514,7 +507,7 @@ public final class ViewRoot extends Handler implements ViewParent,
attrs.restore();
}
}
-
+
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
@@ -560,6 +553,12 @@ public final class ViewRoot extends Handler implements ViewParent,
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
+
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ InputQueue.registerInputChannel(mInputChannel, mInputHandler,
+ Looper.myQueue());
+ }
+
view.assignParent(this);
mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
@@ -1735,6 +1734,14 @@ public final class ViewRoot extends Handler implements ViewParent,
}
mSurface.release();
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ if (mInputChannel != null) {
+ InputQueue.unregisterInputChannel(mInputChannel);
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
+
try {
sWindowSession.remove(mWindow);
} catch (RemoteException e) {
@@ -1841,19 +1848,16 @@ public final class ViewRoot extends Handler implements ViewParent,
boolean callWhenDone = msg.arg1 != 0;
if (event == null) {
- try {
- long timeBeforeGettingEvents;
- if (MEASURE_LATENCY) {
- timeBeforeGettingEvents = System.nanoTime();
- }
+ long timeBeforeGettingEvents;
+ if (MEASURE_LATENCY) {
+ timeBeforeGettingEvents = System.nanoTime();
+ }
- event = sWindowSession.getPendingPointerMove(mWindow);
+ event = getPendingPointerMotionEvent();
- if (MEASURE_LATENCY && event != null) {
- lt.sample("9 Client got events ", System.nanoTime() - event.getEventTimeNano());
- lt.sample("8 Client getting events ", timeBeforeGettingEvents - event.getEventTimeNano());
- }
- } catch (RemoteException e) {
+ if (MEASURE_LATENCY && event != null) {
+ lt.sample("9 Client got events ", System.nanoTime() - event.getEventTimeNano());
+ lt.sample("8 Client getting events ", timeBeforeGettingEvents - event.getEventTimeNano());
}
callWhenDone = false;
}
@@ -1928,14 +1932,9 @@ public final class ViewRoot extends Handler implements ViewParent,
}
} finally {
if (callWhenDone) {
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
- }
- if (event != null) {
- event.recycle();
+ finishMotionEvent();
}
+ recycleMotionEvent(event);
if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
// Let the exception fall through -- the looper will catch
// it and take care of the bad app for us.
@@ -2075,7 +2074,63 @@ public final class ViewRoot extends Handler implements ViewParent,
} break;
}
}
+
+ private void finishKeyEvent(KeyEvent event) {
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ if (mFinishedCallback != null) {
+ mFinishedCallback.run();
+ mFinishedCallback = null;
+ }
+ } else {
+ try {
+ sWindowSession.finishKey(mWindow);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ private void finishMotionEvent() {
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be reachable with native input dispatch.");
+ }
+
+ try {
+ sWindowSession.finishKey(mWindow);
+ } catch (RemoteException e) {
+ }
+ }
+ private void recycleMotionEvent(MotionEvent event) {
+ if (event != null) {
+ event.recycle();
+ }
+ }
+
+ private MotionEvent getPendingPointerMotionEvent() {
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be reachable with native input dispatch.");
+ }
+
+ try {
+ return sWindowSession.getPendingPointerMove(mWindow);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ private MotionEvent getPendingTrackballMotionEvent() {
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be reachable with native input dispatch.");
+ }
+
+ try {
+ return sWindowSession.getPendingTrackballMove(mWindow);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+
/**
* Something in the current window tells us we need to change the touch mode. For
* example, we are not in touch mode, and the user touches the screen.
@@ -2200,10 +2255,7 @@ public final class ViewRoot extends Handler implements ViewParent,
private void deliverTrackballEvent(MotionEvent event, boolean callWhenDone) {
if (event == null) {
- try {
- event = sWindowSession.getPendingTrackballMove(mWindow);
- } catch (RemoteException e) {
- }
+ event = getPendingTrackballMotionEvent();
callWhenDone = false;
}
@@ -2223,14 +2275,9 @@ public final class ViewRoot extends Handler implements ViewParent,
} finally {
if (handled) {
if (callWhenDone) {
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
- }
- if (event != null) {
- event.recycle();
+ finishMotionEvent();
}
+ recycleMotionEvent(event);
// If we reach this, we delivered a trackball event to mView and
// mView consumed it. Because we will not translate the trackball
// event into a key event, touch mode will not exit, so we exit
@@ -2339,13 +2386,8 @@ public final class ViewRoot extends Handler implements ViewParent,
}
} finally {
if (callWhenDone) {
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
- if (event != null) {
- event.recycle();
- }
+ finishMotionEvent();
+ recycleMotionEvent(event);
}
// Let the exception fall through -- the looper will catch
// it and take care of the bad app for us.
@@ -2503,10 +2545,7 @@ public final class ViewRoot extends Handler implements ViewParent,
if (sendDone) {
if (LOCAL_LOGV) Log.v(
"ViewRoot", "Telling window manager key is finished");
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
+ finishKeyEvent(event);
}
return;
}
@@ -2539,10 +2578,7 @@ public final class ViewRoot extends Handler implements ViewParent,
} else if (sendDone) {
if (LOCAL_LOGV) Log.v(
"ViewRoot", "Telling window manager key is finished");
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
+ finishKeyEvent(event);
} else {
Log.w("ViewRoot", "handleFinishedEvent(seq=" + seq
+ " handled=" + handled + " ev=" + event
@@ -2617,10 +2653,7 @@ public final class ViewRoot extends Handler implements ViewParent,
if (sendDone) {
if (LOCAL_LOGV) Log.v(
"ViewRoot", "Telling window manager key is finished");
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
+ finishKeyEvent(event);
}
// Let the exception fall through -- the looper will catch
// it and take care of the bad app for us.
@@ -2798,6 +2831,53 @@ public final class ViewRoot extends Handler implements ViewParent,
msg.obj = ri;
sendMessage(msg);
}
+
+ private Runnable mFinishedCallback;
+
+ private final InputHandler mInputHandler = new InputHandler() {
+ public void handleKey(KeyEvent event, Runnable finishedCallback) {
+ mFinishedCallback = finishedCallback;
+
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ //noinspection ConstantConditions
+ if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
+ if (Config.LOGD) Log.d("keydisp",
+ "===================================================");
+ if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:");
+ debug();
+
+ if (Config.LOGD) Log.d("keydisp",
+ "===================================================");
+ }
+ }
+
+ Message msg = obtainMessage(DISPATCH_KEY);
+ msg.obj = event;
+
+ if (LOCAL_LOGV) Log.v(
+ "ViewRoot", "sending key " + event + " to " + mView);
+
+ sendMessageAtTime(msg, event.getEventTime());
+ }
+
+ public void handleTouch(MotionEvent event, Runnable finishedCallback) {
+ finishedCallback.run();
+
+ Message msg = obtainMessage(DISPATCH_POINTER);
+ msg.obj = event;
+ msg.arg1 = 0;
+ sendMessageAtTime(msg, event.getEventTime());
+ }
+
+ public void handleTrackball(MotionEvent event, Runnable finishedCallback) {
+ finishedCallback.run();
+
+ Message msg = obtainMessage(DISPATCH_TRACKBALL);
+ msg.obj = event;
+ msg.arg1 = 0;
+ sendMessageAtTime(msg, event.getEventTime());
+ }
+ };
public void dispatchKey(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -2968,7 +3048,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
- static class EventCompletion extends Handler {
+ class EventCompletion extends Handler {
final IWindow mWindow;
final KeyEvent mKeyEvent;
final boolean mIsPointer;
@@ -2987,40 +3067,25 @@ public final class ViewRoot extends Handler implements ViewParent,
@Override
public void handleMessage(Message msg) {
if (mKeyEvent != null) {
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
+ finishKeyEvent(mKeyEvent);
} else if (mIsPointer) {
boolean didFinish;
MotionEvent event = mMotionEvent;
if (event == null) {
- try {
- event = sWindowSession.getPendingPointerMove(mWindow);
- } catch (RemoteException e) {
- }
+ event = getPendingPointerMotionEvent();
didFinish = true;
} else {
didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
}
if (!didFinish) {
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
+ finishMotionEvent();
}
} else {
MotionEvent event = mMotionEvent;
if (event == null) {
- try {
- event = sWindowSession.getPendingTrackballMove(mWindow);
- } catch (RemoteException e) {
- }
+ event = getPendingTrackballMotionEvent();
} else {
- try {
- sWindowSession.finishKey(mWindow);
- } catch (RemoteException e) {
- }
+ finishMotionEvent();
}
}
}
@@ -3050,7 +3115,7 @@ public final class ViewRoot extends Handler implements ViewParent,
viewRoot.dispatchKey(event);
} else {
Log.w("ViewRoot.W", "Key event " + event + " but no ViewRoot available!");
- new EventCompletion(mMainLooper, this, event, false, null);
+ viewRoot.new EventCompletion(mMainLooper, this, event, false, null);
}
}
@@ -3064,7 +3129,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
viewRoot.dispatchPointer(event, eventTime, callWhenDone);
} else {
- new EventCompletion(mMainLooper, this, null, true, event);
+ viewRoot.new EventCompletion(mMainLooper, this, null, true, event);
}
}
@@ -3074,7 +3139,7 @@ public final class ViewRoot extends Handler implements ViewParent,
if (viewRoot != null) {
viewRoot.dispatchTrackball(event, eventTime, callWhenDone);
} else {
- new EventCompletion(mMainLooper, this, null, false, event);
+ viewRoot.new EventCompletion(mMainLooper, this, null, false, event);
}
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index b39cb9d..431b786 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -78,6 +78,12 @@ public interface WindowManagerPolicy {
public final static int FLAG_BRIGHT_HERE = 0x20000000;
public final static boolean WATCH_POINTER = false;
+
+ /**
+ * Temporary flag added during the transition to the new native input dispatcher.
+ * This will be removed when the old input dispatch code is deleted.
+ */
+ public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = false;
// flags for interceptKeyTq
/**
@@ -708,6 +714,8 @@ public interface WindowManagerPolicy {
*/
public boolean preprocessInputEventTq(RawInputEvent event);
+ public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
+
/**
* Determine whether a given key code is used to cause an app switch
* to occur (most often the HOME key, also often ENDCALL). If you return
diff --git a/core/java/com/android/internal/view/BaseInputHandler.java b/core/java/com/android/internal/view/BaseInputHandler.java
new file mode 100644
index 0000000..6fe5063
--- /dev/null
+++ b/core/java/com/android/internal/view/BaseInputHandler.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import android.view.InputHandler;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/**
+ * Base do-nothing implementation of an input handler.
+ * @hide
+ */
+public abstract class BaseInputHandler implements InputHandler {
+ public void handleKey(KeyEvent event, Runnable finishedCallback) {
+ finishedCallback.run();
+ }
+
+ public void handleTouch(MotionEvent event, Runnable finishedCallback) {
+ finishedCallback.run();
+ }
+
+ public void handleTrackball(MotionEvent event, Runnable finishedCallback) {
+ finishedCallback.run();
+ }
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d4545d7..d854e87 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -45,6 +45,11 @@ LOCAL_SRC_FILES:= \
android_view_Display.cpp \
android_view_Surface.cpp \
android_view_ViewRoot.cpp \
+ android_view_InputChannel.cpp \
+ android_view_InputQueue.cpp \
+ android_view_InputTarget.cpp \
+ android_view_KeyEvent.cpp \
+ android_view_MotionEvent.cpp \
android_text_AndroidCharacter.cpp \
android_text_AndroidBidi.cpp \
android_text_KeyCharacterMap.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f66ed83..466642a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -162,6 +162,11 @@ extern int register_android_backup_BackupDataOutput(JNIEnv *env);
extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
extern int register_android_backup_BackupHelperDispatcher(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_InputTarget(JNIEnv* env);
+extern int register_android_view_KeyEvent(JNIEnv* env);
+extern int register_android_view_MotionEvent(JNIEnv* env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1288,6 +1293,11 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_backup_BackupHelperDispatcher),
REG_JNI(register_android_app_NativeActivity),
+ REG_JNI(register_android_view_InputChannel),
+ REG_JNI(register_android_view_InputQueue),
+ REG_JNI(register_android_view_InputTarget),
+ REG_JNI(register_android_view_KeyEvent),
+ REG_JNI(register_android_view_MotionEvent),
};
/*
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index 8984057..030d6c7 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -14,325 +14,151 @@
* limitations under the License.
*/
-#define LOG_TAG "MQNative"
+#define LOG_TAG "MessageQueue-JNI"
#include "JNIHelp.h"
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/time.h>
-#include <fcntl.h>
-
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/SystemClock.h>
-#include <utils/Vector.h>
+#include <utils/PollLoop.h>
#include <utils/Log.h>
+#include "android_os_MessageQueue.h"
-using namespace android;
+namespace android {
// ----------------------------------------------------------------------------
static struct {
- jclass mClass;
-
- jfieldID mObject; // native object attached to the DVM MessageQueue
-} gMessageQueueOffsets;
-
-static struct {
- jclass mClass;
- jmethodID mConstructor;
-} gKeyEventOffsets;
-
-// TODO: also MotionEvent offsets etc. a la gKeyEventOffsets
-
-static struct {
- jclass mClass;
- jmethodID mObtain; // obtain(Handler h, int what, Object obj)
-} gMessageOffsets;
-
-// ----------------------------------------------------------------------------
+ jclass clazz;
-static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
-{
- if (jniThrowException(env, exc, msg) != 0)
- assert(false);
-}
+ jfieldID mPtr; // native object attached to the DVM MessageQueue
+} gMessageQueueClassInfo;
// ----------------------------------------------------------------------------
-class MessageQueueNative {
+class NativeMessageQueue {
public:
- MessageQueueNative(int readSocket, int writeSocket);
- ~MessageQueueNative();
-
- // select on all FDs until the designated time; forever if wakeupTime is < 0
- int waitForSignal(jobject mqueue, jlong wakeupTime);
+ NativeMessageQueue();
+ ~NativeMessageQueue();
- // signal the queue-ready pipe
- void signalQueuePipe();
+ inline sp<PollLoop> getPollLoop() { return mPollLoop; }
- // Specify a new input pipe, passing in responsibility for the socket fd and
- // ashmem region
- int registerInputPipe(JNIEnv* env, int socketFd, int memRegionFd, jobject handler);
-
- // Forget about this input pipe, closing the socket and ashmem region as well
- int unregisterInputPipe(JNIEnv* env, int socketFd);
-
- size_t numRegisteredPipes() const { return mInputPipes.size(); }
+ bool pollOnce(int timeoutMillis);
+ void wake();
private:
- struct InputPipe {
- int fd;
- int region;
- jobject handler;
-
- InputPipe() {}
- InputPipe(int _fd, int _r, jobject _h) : fd(_fd), region(_r), handler(_h) {}
- };
-
- // consume an event from a socket, put it on the DVM MessageQueue indicated,
- // and notify the other end of the pipe that we've consumed it.
- void queueEventFromPipe(const InputPipe& pipe, jobject mqueue);
-
- int mQueueReadFd, mQueueWriteFd;
- Vector<InputPipe> mInputPipes;
+ sp<PollLoop> mPollLoop;
};
-MessageQueueNative::MessageQueueNative(int readSocket, int writeSocket)
- : mQueueReadFd(readSocket), mQueueWriteFd(writeSocket) {
-}
+// ----------------------------------------------------------------------------
-MessageQueueNative::~MessageQueueNative() {
+NativeMessageQueue::NativeMessageQueue() {
+ mPollLoop = new PollLoop();
}
-int MessageQueueNative::waitForSignal(jobject mqueue, jlong timeoutMillis) {
- struct timeval tv, *timeout;
- fd_set fdset;
-
- if (timeoutMillis < 0) {
- timeout = NULL;
- } else {
- if (timeoutMillis == 0) {
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- } else {
- tv.tv_sec = (timeoutMillis / 1000);
- tv.tv_usec = (timeoutMillis - (1000 * tv.tv_sec)) * 1000;
- }
- timeout = &tv;
- }
-
- // always rebuild the fd set from scratch
- FD_ZERO(&fdset);
-
- // the queue signalling pipe
- FD_SET(mQueueReadFd, &fdset);
- int maxFd = mQueueReadFd;
-
- // and the input sockets themselves
- for (size_t i = 0; i < mInputPipes.size(); i++) {
- FD_SET(mInputPipes[i].fd, &fdset);
- if (maxFd < mInputPipes[i].fd) {
- maxFd = mInputPipes[i].fd;
- }
- }
-
- // now wait
- int res = select(maxFd + 1, &fdset, NULL, NULL, timeout);
-
- // Error? Just return it and bail
- if (res < 0) return res;
-
- // What happened -- timeout or actual data arrived?
- if (res == 0) {
- // select() returned zero, which means we timed out, which means that it's time
- // to deliver the head element that was already on the queue. Just fall through
- // without doing anything else.
- } else {
- // Data (or a queue signal) arrived!
- //
- // If it's data, pull the data off the pipe, build a new Message with it, put it on
- // the DVM-side MessageQueue (pointed to by the 'mqueue' parameter), then proceed
- // into the queue-signal case.
- //
- // If a queue signal arrived, just consume any data pending in that pipe and
- // fall out.
- bool queue_signalled = (FD_ISSET(mQueueReadFd, &fdset) != 0);
-
- for (size_t i = 0; i < mInputPipes.size(); i++) {
- if (FD_ISSET(mInputPipes[i].fd, &fdset)) {
- queueEventFromPipe(mInputPipes[i], mqueue);
- queue_signalled = true; // we know a priori that queueing the event does this
- }
- }
-
- // Okay, stuff went on the queue. Consume the contents of the signal pipe
- // now that we're awake and about to start dispatching messages again.
- if (queue_signalled) {
- uint8_t buf[16];
- ssize_t nRead;
- do {
- nRead = read(mQueueReadFd, buf, sizeof(buf));
- } while (nRead > 0); // in nonblocking mode we'll get -1 when it's drained
- }
- }
-
- return 0;
+NativeMessageQueue::~NativeMessageQueue() {
}
-// signals to the queue pipe are one undefined byte. it's just a "data has arrived" token
-// and the pipe is drained on receipt of at least one signal
-void MessageQueueNative::signalQueuePipe() {
- int dummy[1];
- write(mQueueWriteFd, dummy, 1);
+bool NativeMessageQueue::pollOnce(int timeoutMillis) {
+ return mPollLoop->pollOnce(timeoutMillis);
}
-void MessageQueueNative::queueEventFromPipe(const InputPipe& inPipe, jobject mqueue) {
- // !!! TODO: read the event data from the InputPipe's ashmem region, convert it to a DVM
- // event object of the proper type [MotionEvent or KeyEvent], create a Message holding
- // it as appropriate, point the Message to the Handler associated with this InputPipe,
- // and call up to the DVM MessageQueue implementation to enqueue it for delivery.
+void NativeMessageQueue::wake() {
+ mPollLoop->wake();
}
-// the number of registered pipes on success; < 0 on error
-int MessageQueueNative::registerInputPipe(JNIEnv* env,
- int socketFd, int memRegionFd, jobject handler) {
- // make sure this fd is not already known to us
- for (size_t i = 0; i < mInputPipes.size(); i++) {
- if (mInputPipes[i].fd == socketFd) {
- LOGE("Attempt to re-register input fd %d", socketFd);
- return -1;
- }
- }
+// ----------------------------------------------------------------------------
- mInputPipes.push( InputPipe(socketFd, memRegionFd, env->NewGlobalRef(handler)) );
- return mInputPipes.size();
+static NativeMessageQueue* android_os_MessageQueue_getNativeMessageQueue(JNIEnv* env,
+ jobject messageQueueObj) {
+ jint intPtr = env->GetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr);
+ return reinterpret_cast<NativeMessageQueue*>(intPtr);
}
-// Remove an input pipe from our bookkeeping. Also closes the socket and ashmem
-// region file descriptor!
-//
-// returns the number of remaining input pipes on success; < 0 on error
-int MessageQueueNative::unregisterInputPipe(JNIEnv* env, int socketFd) {
- for (size_t i = 0; i < mInputPipes.size(); i++) {
- if (mInputPipes[i].fd == socketFd) {
- close(mInputPipes[i].fd);
- close(mInputPipes[i].region);
- env->DeleteGlobalRef(mInputPipes[i].handler);
- mInputPipes.removeAt(i);
- return mInputPipes.size();
- }
- }
- LOGW("Tried to unregister input pipe %d but not found!", socketFd);
- return -1;
+static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
+ NativeMessageQueue* nativeMessageQueue) {
+ env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
+ reinterpret_cast<jint>(nativeMessageQueue));
}
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-static void android_os_MessageQueue_init(JNIEnv* env, jobject obj) {
- // Create the pipe
- int fds[2];
- int err = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
- if (err != 0) {
- doThrow(env, "java/lang/RuntimeException", "Unable to create socket pair");
- }
+sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj) {
+ NativeMessageQueue* nativeMessageQueue =
+ android_os_MessageQueue_getNativeMessageQueue(env, messageQueueObj);
+ return nativeMessageQueue != NULL ? nativeMessageQueue->getPollLoop() : NULL;
+}
- MessageQueueNative *mqn = new MessageQueueNative(fds[0], fds[1]);
- if (mqn == NULL) {
- close(fds[0]);
- close(fds[1]);
- doThrow(env, "java/lang/RuntimeException", "Unable to allocate native queue");
+static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
+ NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
+ if (! nativeMessageQueue) {
+ jniThrowRuntimeException(env, "Unable to allocate native queue");
+ return;
}
- int flags = fcntl(fds[0], F_GETFL);
- fcntl(fds[0], F_SETFL, flags | O_NONBLOCK);
- flags = fcntl(fds[1], F_GETFL);
- fcntl(fds[1], F_SETFL, flags | O_NONBLOCK);
-
- env->SetIntField(obj, gMessageQueueOffsets.mObject, (jint)mqn);
+ android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
-static void android_os_MessageQueue_signal(JNIEnv* env, jobject obj) {
- MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject);
- if (mqn != NULL) {
- mqn->signalQueuePipe();
- } else {
- doThrow(env, "java/lang/IllegalStateException", "Queue not initialized");
+static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jobject obj) {
+ NativeMessageQueue* nativeMessageQueue =
+ android_os_MessageQueue_getNativeMessageQueue(env, obj);
+ if (nativeMessageQueue) {
+ android_os_MessageQueue_setNativeMessageQueue(env, obj, NULL);
+ delete nativeMessageQueue;
}
}
-static int android_os_MessageQueue_waitForNext(JNIEnv* env, jobject obj, jlong when) {
- MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject);
- if (mqn != NULL) {
- int res = mqn->waitForSignal(obj, when);
- return res; // the DVM event, if any, has been constructed and queued now
- }
-
- return -1;
+static void throwQueueNotInitialized(JNIEnv* env) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Message queue not initialized");
}
-static void android_os_MessageQueue_registerInputStream(JNIEnv* env, jobject obj,
- jint socketFd, jint regionFd, jobject handler) {
- MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject);
- if (mqn != NULL) {
- mqn->registerInputPipe(env, socketFd, regionFd, handler);
- } else {
- doThrow(env, "java/lang/IllegalStateException", "Queue not initialized");
+static jboolean android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
+ jint timeoutMillis) {
+ NativeMessageQueue* nativeMessageQueue =
+ android_os_MessageQueue_getNativeMessageQueue(env, obj);
+ if (! nativeMessageQueue) {
+ throwQueueNotInitialized(env);
+ return false;
}
+ return nativeMessageQueue->pollOnce(timeoutMillis);
}
-static void android_os_MessageQueue_unregisterInputStream(JNIEnv* env, jobject obj,
- jint socketFd) {
- MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject);
- if (mqn != NULL) {
- mqn->unregisterInputPipe(env, socketFd);
- } else {
- doThrow(env, "java/lang/IllegalStateException", "Queue not initialized");
+static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj) {
+ NativeMessageQueue* nativeMessageQueue =
+ android_os_MessageQueue_getNativeMessageQueue(env, obj);
+ if (! nativeMessageQueue) {
+ throwQueueNotInitialized(env);
+ return;
}
+ return nativeMessageQueue->wake();
}
// ----------------------------------------------------------------------------
-const char* const kKeyEventPathName = "android/view/KeyEvent";
-const char* const kMessagePathName = "android/os/Message";
-const char* const kMessageQueuePathName = "android/os/MessageQueue";
-
static JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
- { "nativeInit", "()V", (void*)android_os_MessageQueue_init },
- { "nativeSignal", "()V", (void*)android_os_MessageQueue_signal },
- { "nativeWaitForNext", "(J)I", (void*)android_os_MessageQueue_waitForNext },
- { "nativeRegisterInputStream", "(IILandroid/os/Handler;)V", (void*)android_os_MessageQueue_registerInputStream },
- { "nativeUnregisterInputStream", "(I)V", (void*)android_os_MessageQueue_unregisterInputStream },
+ { "nativeInit", "()V", (void*)android_os_MessageQueue_nativeInit },
+ { "nativeDestroy", "()V", (void*)android_os_MessageQueue_nativeDestroy },
+ { "nativePollOnce", "(I)Z", (void*)android_os_MessageQueue_nativePollOnce },
+ { "nativeWake", "()V", (void*)android_os_MessageQueue_nativeWake }
};
+#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_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
int register_android_os_MessageQueue(JNIEnv* env) {
- jclass clazz;
+ int res = jniRegisterNativeMethods(env, "android/os/MessageQueue",
+ gMessageQueueMethods, NELEM(gMessageQueueMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ FIND_CLASS(gMessageQueueClassInfo.clazz, "android/os/MessageQueue");
- clazz = env->FindClass(kMessageQueuePathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.MessageQueue");
- gMessageQueueOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gMessageQueueOffsets.mObject = env->GetFieldID(clazz, "mObject", "I");
- assert(gMessageQueueOffsets.mObject);
-
- clazz = env->FindClass(kMessagePathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Message");
- gMessageOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gMessageOffsets.mObtain = env->GetStaticMethodID(clazz, "obtain",
- "(Landroid/os/Handler;ILjava/lang/Object;)Landroid/os/Message;");
- assert(gMessageOffsets.mObtain);
-
- clazz = env->FindClass(kKeyEventPathName);
- LOG_FATAL_IF(clazz == NULL, "Unable to find class android.view.KeyEvent");
- gKeyEventOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
- gKeyEventOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(JJIIIIIII)V");
- assert(gKeyEventOffsets.mConstructor);
+ GET_FIELD_ID(gMessageQueueClassInfo.mPtr, gMessageQueueClassInfo.clazz,
+ "mPtr", "I");
- return AndroidRuntime::registerNativeMethods(env, kMessageQueuePathName,
- gMessageQueueMethods, NELEM(gMessageQueueMethods));
+ return 0;
}
-
-}; // end of namespace android
+} // namespace android
diff --git a/core/jni/android_os_MessageQueue.h b/core/jni/android_os_MessageQueue.h
new file mode 100644
index 0000000..5c742e2
--- /dev/null
+++ b/core/jni/android_os_MessageQueue.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_OS_MESSAGEQUEUE_H
+#define _ANDROID_OS_MESSAGEQUEUE_H
+
+#include "jni.h"
+
+namespace android {
+
+class PollLoop;
+
+extern sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj);
+
+} // namespace android
+
+#endif // _ANDROID_OS_MESSAGEQUEUE_H
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
new file mode 100644
index 0000000..47bb073
--- /dev/null
+++ b/core/jni/android_view_InputChannel.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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 "InputChannel-JNI"
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+#include <ui/InputTransport.h>
+#include "android_view_InputChannel.h"
+#include "android_util_Binder.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+
+ jfieldID mPtr; // native object attached to the DVM InputChannel
+ jmethodID ctor;
+} gInputChannelClassInfo;
+
+// ----------------------------------------------------------------------------
+
+class NativeInputChannel {
+public:
+ NativeInputChannel(const sp<InputChannel>& inputChannel);
+ ~NativeInputChannel();
+
+ inline sp<InputChannel> getInputChannel() { return mInputChannel; }
+
+ void setDisposeCallback(InputChannelObjDisposeCallback callback, void* data);
+ void invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj);
+
+private:
+ sp<InputChannel> mInputChannel;
+ InputChannelObjDisposeCallback mDisposeCallback;
+ void* mDisposeData;
+};
+
+// ----------------------------------------------------------------------------
+
+NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
+ mInputChannel(inputChannel), mDisposeCallback(NULL) {
+}
+
+NativeInputChannel::~NativeInputChannel() {
+}
+
+void NativeInputChannel::setDisposeCallback(InputChannelObjDisposeCallback callback, void* data) {
+ mDisposeCallback = callback;
+ mDisposeData = data;
+}
+
+void NativeInputChannel::invokeAndRemoveDisposeCallback(JNIEnv* env, jobject obj) {
+ if (mDisposeCallback) {
+ mDisposeCallback(env, obj, mInputChannel, mDisposeData);
+ mDisposeCallback = NULL;
+ mDisposeData = NULL;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
+ jobject inputChannelObj) {
+ jint intPtr = env->GetIntField(inputChannelObj, gInputChannelClassInfo.mPtr);
+ return reinterpret_cast<NativeInputChannel*>(intPtr);
+}
+
+static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
+ NativeInputChannel* nativeInputChannel) {
+ env->SetIntField(inputChannelObj, gInputChannelClassInfo.mPtr,
+ reinterpret_cast<jint>(nativeInputChannel));
+}
+
+sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env, jobject inputChannelObj) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
+ return nativeInputChannel != NULL ? nativeInputChannel->getInputChannel() : NULL;
+}
+
+void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
+ InputChannelObjDisposeCallback callback, void* data) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, inputChannelObj);
+ if (nativeInputChannel == NULL) {
+ LOGW("Cannot set dispose callback because input channel object has not been initialized.");
+ } else {
+ nativeInputChannel->setDisposeCallback(callback, data);
+ }
+}
+
+static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
+ NativeInputChannel* nativeInputChannel) {
+ jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
+ gInputChannelClassInfo.ctor);
+ android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
+ return inputChannelObj;
+}
+
+static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
+ jclass clazz, jstring nameObj) {
+ const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
+ String8 name(nameChars);
+ env->ReleaseStringUTFChars(nameObj, nameChars);
+
+ InputChannel* serverChannel;
+ InputChannel* clientChannel;
+ status_t result = InputChannel::openInputChannelPair(name, & serverChannel, & clientChannel);
+
+ if (result) {
+ LOGE("Could not open input channel pair. status=%d", result);
+ jniThrowRuntimeException(env, "Could not open input channel pair.");
+ return NULL;
+ }
+
+ // TODO more robust error checking
+ jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
+ new NativeInputChannel(serverChannel));
+ jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
+ new NativeInputChannel(clientChannel));
+
+ jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
+ env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
+ env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
+ return channelPair;
+}
+
+static void android_view_InputChannel_nativeDispose(JNIEnv* env, jobject obj, jboolean finalized) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (nativeInputChannel) {
+ if (finalized) {
+ LOGW("Input channel object '%s' was finalized without being disposed!",
+ nativeInputChannel->getInputChannel()->getName().string());
+ }
+
+ nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
+
+ android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+ delete nativeInputChannel;
+ }
+}
+
+static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
+ jobject otherObj) {
+ if (android_view_InputChannel_getInputChannel(env, otherObj) != NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Other object already has a native input channel.");
+ return;
+ }
+
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
+ android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
+}
+
+static void android_view_InputChannel_nativeReadFromParcel(JNIEnv* env, jobject obj,
+ jobject parcelObj) {
+ if (android_view_InputChannel_getInputChannel(env, obj) != NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "This object already has a native input channel.");
+ return;
+ }
+
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
+ if (parcel) {
+ bool isInitialized = parcel->readInt32();
+ if (isInitialized) {
+ String8 name = parcel->readString8();
+ int32_t ashmemFd = dup(parcel->readFileDescriptor());
+ int32_t receivePipeFd = dup(parcel->readFileDescriptor());
+ int32_t sendPipeFd = dup(parcel->readFileDescriptor());
+ if (ashmemFd < 0 || receivePipeFd < 0 || sendPipeFd < 0) {
+ if (ashmemFd >= 0) ::close(ashmemFd);
+ if (receivePipeFd >= 0) ::close(receivePipeFd);
+ if (sendPipeFd >= 0) ::close(sendPipeFd);
+ jniThrowRuntimeException(env,
+ "Could not read input channel file descriptors from parcel.");
+ return;
+ }
+
+ InputChannel* inputChannel = new InputChannel(name, ashmemFd,
+ receivePipeFd, sendPipeFd);
+ NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
+
+ android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
+ }
+ }
+}
+
+static void android_view_InputChannel_nativeWriteToParcel(JNIEnv* env, jobject obj,
+ jobject parcelObj) {
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
+ if (parcel) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (nativeInputChannel) {
+ sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
+
+ parcel->writeInt32(1);
+ parcel->writeString8(inputChannel->getName());
+ parcel->writeDupFileDescriptor(inputChannel->getAshmemFd());
+ parcel->writeDupFileDescriptor(inputChannel->getReceivePipeFd());
+ parcel->writeDupFileDescriptor(inputChannel->getSendPipeFd());
+ } else {
+ parcel->writeInt32(0);
+ }
+ }
+}
+
+static jstring android_view_InputChannel_nativeGetName(JNIEnv* env, jobject obj) {
+ NativeInputChannel* nativeInputChannel =
+ android_view_InputChannel_getNativeInputChannel(env, obj);
+ if (! nativeInputChannel) {
+ return NULL;
+ }
+
+ jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string());
+ return name;
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gInputChannelMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeOpenInputChannelPair", "(Ljava/lang/String;)[Landroid/view/InputChannel;",
+ (void*)android_view_InputChannel_nativeOpenInputChannelPair },
+ { "nativeDispose", "(Z)V",
+ (void*)android_view_InputChannel_nativeDispose },
+ { "nativeTransferTo", "(Landroid/view/InputChannel;)V",
+ (void*)android_view_InputChannel_nativeTransferTo },
+ { "nativeReadFromParcel", "(Landroid/os/Parcel;)V",
+ (void*)android_view_InputChannel_nativeReadFromParcel },
+ { "nativeWriteToParcel", "(Landroid/os/Parcel;)V",
+ (void*)android_view_InputChannel_nativeWriteToParcel },
+ { "nativeGetName", "()Ljava/lang/String;",
+ (void*)android_view_InputChannel_nativeGetName },
+};
+
+#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);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_view_InputChannel(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "android/view/InputChannel",
+ gInputChannelMethods, NELEM(gInputChannelMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ FIND_CLASS(gInputChannelClassInfo.clazz, "android/view/InputChannel");
+
+ GET_FIELD_ID(gInputChannelClassInfo.mPtr, gInputChannelClassInfo.clazz,
+ "mPtr", "I");
+
+ GET_METHOD_ID(gInputChannelClassInfo.ctor, gInputChannelClassInfo.clazz,
+ "<init>", "()V");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h
new file mode 100644
index 0000000..ac1defb
--- /dev/null
+++ b/core/jni/android_view_InputChannel.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_VIEW_INPUTCHANNEL_H
+#define _ANDROID_VIEW_INPUTCHANNEL_H
+
+#include "jni.h"
+
+namespace android {
+
+class InputChannel;
+
+typedef void (*InputChannelObjDisposeCallback)(JNIEnv* env, jobject inputChannelObj,
+ const sp<InputChannel>& inputChannel, void* data);
+
+extern sp<InputChannel> android_view_InputChannel_getInputChannel(JNIEnv* env,
+ jobject inputChannelObj);
+
+/* Sets a callback that is invoked when the InputChannel DVM object is disposed (or finalized).
+ * This is used to automatically dispose of other native objects in the input dispatcher
+ * and input queue to prevent memory leaks. */
+extern void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
+ InputChannelObjDisposeCallback callback, void* data = NULL);
+
+} // namespace android
+
+#endif // _ANDROID_OS_INPUTCHANNEL_H
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
new file mode 100644
index 0000000..9cbde25
--- /dev/null
+++ b/core/jni/android_view_InputQueue.cpp
@@ -0,0 +1,471 @@
+/*
+ * 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 1
+
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <utils/PollLoop.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 dispatchKeyEvent;
+ jmethodID dispatchMotionEvent;
+} gInputQueueClassInfo;
+
+// ----------------------------------------------------------------------------
+
+class NativeInputQueue {
+public:
+ NativeInputQueue();
+ virtual ~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 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(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop);
+
+ inline const char* getInputChannelName() { return inputChannel->getName().string(); }
+
+ Status status;
+
+ sp<InputChannel> inputChannel;
+ InputConsumer inputConsumer;
+ sp<PollLoop> pollLoop;
+ 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.
+ uint32_t messageSeqNum;
+
+ // True if a message has been received from the publisher but not yet finished.
+ bool messageInProgress;
+ };
+
+ Mutex mLock;
+ KeyedVector<int32_t, sp<Connection> > mConnectionsByReceiveFd;
+
+ static void handleInputChannelDisposed(JNIEnv* env,
+ jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data);
+
+ static bool handleReceiveCallback(int receiveFd, int events, void* data);
+
+ static jlong generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum);
+
+ static void parseFinishedToken(jlong finishedToken,
+ int32_t* outReceiveFd, uint32_t* outMessageIndex);
+};
+
+// ----------------------------------------------------------------------------
+
+NativeInputQueue::NativeInputQueue() {
+}
+
+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;
+ }
+
+ sp<PollLoop> pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueueObj);
+
+ int receiveFd;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ receiveFd = inputChannel->getReceivePipeFd();
+ if (mConnectionsByReceiveFd.indexOfKey(receiveFd) >= 0) {
+ LOGW("Attempted to register already registered input channel '%s'",
+ inputChannel->getName().string());
+ return BAD_VALUE;
+ }
+
+ sp<Connection> connection = new Connection(inputChannel, pollLoop);
+ 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);
+
+ mConnectionsByReceiveFd.add(receiveFd, connection);
+ } // release lock
+
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
+ handleInputChannelDisposed, this);
+
+ pollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, NULL);
+ 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;
+ }
+
+ int32_t receiveFd;
+ sp<Connection> connection;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ receiveFd = inputChannel->getReceivePipeFd();
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ if (connectionIndex < 0) {
+ LOGW("Attempted to unregister already unregistered input channel '%s'",
+ inputChannel->getName().string());
+ return BAD_VALUE;
+ }
+
+ connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ mConnectionsByReceiveFd.removeItemsAt(connectionIndex);
+
+ connection->status = Connection::STATUS_ZOMBIE;
+
+ env->DeleteGlobalRef(connection->inputHandlerObjGlobal);
+ connection->inputHandlerObjGlobal = NULL;
+ } // release lock
+
+ android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
+
+ connection->pollLoop->removeCallback(receiveFd);
+ return OK;
+}
+
+status_t NativeInputQueue::finished(JNIEnv* env, jlong finishedToken, bool ignoreSpuriousFinish) {
+ int32_t receiveFd;
+ uint32_t messageSeqNum;
+ parseFinishedToken(finishedToken, &receiveFd, &messageSeqNum);
+
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(receiveFd);
+ if (connectionIndex < 0) {
+ if (! ignoreSpuriousFinish) {
+ LOGW("Attempted to finish input on channel that is no longer registered.");
+ }
+ return DEAD_OBJECT;
+ }
+
+ sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (messageSeqNum != connection->messageSeqNum || ! connection->messageInProgress) {
+ if (! ignoreSpuriousFinish) {
+ LOGW("Attempted to finish input twice on channel '%s'.",
+ connection->getInputChannelName());
+ }
+ return INVALID_OPERATION;
+ }
+
+ connection->messageInProgress = false;
+
+ status_t status = connection->inputConsumer.sendFinishedSignal();
+ 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);
+}
+
+bool 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 false; // remove the callback
+ }
+
+ connection = q->mConnectionsByReceiveFd.valueAt(connectionIndex);
+ if (events & (POLLERR | POLLHUP | POLLNVAL)) {
+ LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. "
+ "events=0x%x", connection->getInputChannelName(), events);
+ return false; // remove the callback
+ }
+
+ if (! (events & POLLIN)) {
+ LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
+ "events=0x%x", connection->getInputChannelName(), events);
+ return true;
+ }
+
+ status_t status = connection->inputConsumer.receiveDispatchSignal();
+ if (status) {
+ LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
+ connection->getInputChannelName(), status);
+ return false; // remove the callback
+ }
+
+ if (connection->messageInProgress) {
+ LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
+ connection->getInputChannelName());
+ return true;
+ }
+
+ 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();
+ return true;
+ }
+
+ connection->messageInProgress = true;
+ connection->messageSeqNum += 1;
+
+ finishedToken = generateFinishedToken(receiveFd, 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();
+ int32_t inputEventNature = inputEvent->getNature();
+
+ jobject inputEventObj;
+ jmethodID dispatchMethodId;
+ switch (inputEventType) {
+ case INPUT_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));
+ dispatchMethodId = gInputQueueClassInfo.dispatchKeyEvent;
+ break;
+
+ case INPUT_EVENT_TYPE_MOTION:
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Received motion event.", connection->getInputChannelName());
+#endif
+ inputEventObj = android_view_MotionEvent_fromNative(env,
+ static_cast<MotionEvent*>(inputEvent));
+ dispatchMethodId = gInputQueueClassInfo.dispatchMotionEvent;
+ 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);
+ return true;
+ }
+
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("Invoking input handler.");
+#endif
+ env->CallStaticVoidMethod(gInputQueueClassInfo.clazz,
+ dispatchMethodId, inputHandlerObjLocal, inputEventObj,
+ jint(inputEventNature), 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, true /*ignoreSpuriousFinish*/);
+ }
+
+ env->DeleteLocalRef(inputEventObj);
+ env->DeleteLocalRef(inputHandlerObjLocal);
+ return true;
+}
+
+jlong NativeInputQueue::generateFinishedToken(int32_t receiveFd, int32_t messageSeqNum) {
+ return (jlong(receiveFd) << 32) | jlong(messageSeqNum);
+}
+
+void NativeInputQueue::parseFinishedToken(jlong finishedToken,
+ int32_t* outReceiveFd, uint32_t* outMessageIndex) {
+ *outReceiveFd = int32_t(finishedToken >> 32);
+ *outMessageIndex = uint32_t(finishedToken & 0xffffffff);
+}
+
+// ----------------------------------------------------------------------------
+
+NativeInputQueue::Connection::Connection(const sp<InputChannel>& inputChannel, const sp<PollLoop>& pollLoop) :
+ status(STATUS_NORMAL), inputChannel(inputChannel), inputConsumer(inputChannel),
+ pollLoop(pollLoop), 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) {
+ jniThrowRuntimeException(env, "Failed to register input channel. "
+ "Check logs for details.");
+ }
+}
+
+static void android_view_InputQueue_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
+ jobject inputChannelObj) {
+ status_t status = gNativeInputQueue.unregisterInputChannel(env, inputChannelObj);
+ if (status) {
+ jniThrowRuntimeException(env, "Failed to unregister input channel. "
+ "Check logs for details.");
+ }
+}
+
+static void android_view_InputQueue_nativeFinished(JNIEnv* env, jclass clazz,
+ jlong finishedToken) {
+ status_t status = gNativeInputQueue.finished(
+ env, finishedToken, false /*ignoreSpuriousFinish*/);
+ if (status) {
+ jniThrowRuntimeException(env, "Failed to finish input event. "
+ "Check logs for details.");
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+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", "(J)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.dispatchKeyEvent, gInputQueueClassInfo.clazz,
+ "dispatchKeyEvent",
+ "(Landroid/view/InputHandler;Landroid/view/KeyEvent;IJ)V");
+
+ GET_STATIC_METHOD_ID(gInputQueueClassInfo.dispatchMotionEvent, gInputQueueClassInfo.clazz,
+ "dispatchMotionEvent",
+ "(Landroid/view/InputHandler;Landroid/view/MotionEvent;IJ)V");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_InputTarget.cpp b/core/jni/android_view_InputTarget.cpp
new file mode 100644
index 0000000..e2a1f23
--- /dev/null
+++ b/core/jni/android_view_InputTarget.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 "InputTarget-JNI"
+
+#include "JNIHelp.h"
+
+#include <utils/Log.h>
+#include <ui/InputDispatchPolicy.h>
+#include <ui/InputTransport.h>
+#include "android_view_InputTarget.h"
+#include "android_view_InputChannel.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+
+ jfieldID mInputChannel;
+ jfieldID mFlags;
+ jfieldID mTimeoutNanos;
+ jfieldID mXOffset;
+ jfieldID mYOffset;
+} gInputTargetClassInfo;
+
+// ----------------------------------------------------------------------------
+
+void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
+ InputTarget* outInputTarget) {
+ jobject inputChannelObj = env->GetObjectField(inputTargetObj,
+ gInputTargetClassInfo.mInputChannel);
+ jint flags = env->GetIntField(inputTargetObj,
+ gInputTargetClassInfo.mFlags);
+ jlong timeoutNanos = env->GetLongField(inputTargetObj,
+ gInputTargetClassInfo.mTimeoutNanos);
+ jfloat xOffset = env->GetFloatField(inputTargetObj,
+ gInputTargetClassInfo.mXOffset);
+ jfloat yOffset = env->GetFloatField(inputTargetObj,
+ gInputTargetClassInfo.mYOffset);
+
+ outInputTarget->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
+ outInputTarget->flags = flags;
+ outInputTarget->timeout = timeoutNanos;
+ outInputTarget->xOffset = xOffset;
+ outInputTarget->yOffset = yOffset;
+
+ env->DeleteLocalRef(inputChannelObj);
+}
+
+// ----------------------------------------------------------------------------
+
+#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_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_view_InputTarget(JNIEnv* env) {
+ FIND_CLASS(gInputTargetClassInfo.clazz, "android/view/InputTarget");
+
+ GET_FIELD_ID(gInputTargetClassInfo.mInputChannel, gInputTargetClassInfo.clazz,
+ "mInputChannel", "Landroid/view/InputChannel;");
+
+ GET_FIELD_ID(gInputTargetClassInfo.mFlags, gInputTargetClassInfo.clazz,
+ "mFlags", "I");
+
+ GET_FIELD_ID(gInputTargetClassInfo.mTimeoutNanos, gInputTargetClassInfo.clazz,
+ "mTimeoutNanos", "J");
+
+ GET_FIELD_ID(gInputTargetClassInfo.mXOffset, gInputTargetClassInfo.clazz,
+ "mXOffset", "F");
+
+ GET_FIELD_ID(gInputTargetClassInfo.mYOffset, gInputTargetClassInfo.clazz,
+ "mYOffset", "F");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_InputTarget.h b/core/jni/android_view_InputTarget.h
new file mode 100644
index 0000000..9230b1b
--- /dev/null
+++ b/core/jni/android_view_InputTarget.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_VIEW_INPUTTARGET_H
+#define _ANDROID_VIEW_INPUTTARGET_H
+
+#include "jni.h"
+
+namespace android {
+
+class InputTarget;
+
+extern void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
+ InputTarget* outInputTarget);
+
+} // namespace android
+
+#endif // _ANDROID_OS_INPUTTARGET_H
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
new file mode 100644
index 0000000..df3b952
--- /dev/null
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "KeyEvent-JNI"
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <ui/Input.h>
+#include "android_view_KeyEvent.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+
+ jmethodID ctor;
+
+ jfieldID mMetaState;
+ jfieldID mAction;
+ jfieldID mKeyCode;
+ jfieldID mScanCode;
+ jfieldID mRepeatCount;
+ jfieldID mDeviceId;
+ jfieldID mFlags;
+ jfieldID mDownTime;
+ jfieldID mEventTime;
+ jfieldID mCharacters;
+} gKeyEventClassInfo;
+
+// ----------------------------------------------------------------------------
+
+jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event) {
+ return env->NewObject(gKeyEventClassInfo.clazz, gKeyEventClassInfo.ctor,
+ nanoseconds_to_milliseconds(event->getDownTime()),
+ nanoseconds_to_milliseconds(event->getEventTime()),
+ event->getAction(),
+ event->getKeyCode(),
+ event->getRepeatCount(),
+ event->getMetaState(),
+ event->getDeviceId(),
+ event->getScanCode(),
+ event->getFlags());
+}
+
+void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature,
+ KeyEvent* event) {
+ jint metaState = env->GetIntField(eventObj, gKeyEventClassInfo.mMetaState);
+ jint action = env->GetIntField(eventObj, gKeyEventClassInfo.mAction);
+ jint keyCode = env->GetIntField(eventObj, gKeyEventClassInfo.mKeyCode);
+ jint scanCode = env->GetIntField(eventObj, gKeyEventClassInfo.mScanCode);
+ jint repeatCount = env->GetIntField(eventObj, gKeyEventClassInfo.mRepeatCount);
+ jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
+ jint flags = env->GetIntField(eventObj, gKeyEventClassInfo.mFlags);
+ jlong downTime = env->GetLongField(eventObj, gKeyEventClassInfo.mDownTime);
+ jlong eventTime = env->GetLongField(eventObj, gKeyEventClassInfo.mEventTime);
+
+ event->initialize(deviceId, nature, action, flags, keyCode, scanCode, metaState, repeatCount,
+ milliseconds_to_nanoseconds(downTime),
+ milliseconds_to_nanoseconds(eventTime));
+}
+
+// ----------------------------------------------------------------------------
+
+#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, fieldDescriptor) \
+ var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method" methodName);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_view_KeyEvent(JNIEnv* env) {
+ FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent");
+
+ GET_METHOD_ID(gKeyEventClassInfo.ctor, gKeyEventClassInfo.clazz,
+ "<init>", "(JJIIIIIII)V");
+
+ GET_FIELD_ID(gKeyEventClassInfo.mMetaState, gKeyEventClassInfo.clazz,
+ "mMetaState", "I");
+ GET_FIELD_ID(gKeyEventClassInfo.mAction, gKeyEventClassInfo.clazz,
+ "mAction", "I");
+ GET_FIELD_ID(gKeyEventClassInfo.mKeyCode, gKeyEventClassInfo.clazz,
+ "mKeyCode", "I");
+ GET_FIELD_ID(gKeyEventClassInfo.mScanCode, gKeyEventClassInfo.clazz,
+ "mScanCode", "I");
+ GET_FIELD_ID(gKeyEventClassInfo.mRepeatCount, gKeyEventClassInfo.clazz,
+ "mRepeatCount", "I");
+ GET_FIELD_ID(gKeyEventClassInfo.mDeviceId, gKeyEventClassInfo.clazz,
+ "mDeviceId", "I");
+ GET_FIELD_ID(gKeyEventClassInfo.mFlags, gKeyEventClassInfo.clazz,
+ "mFlags", "I");
+ GET_FIELD_ID(gKeyEventClassInfo.mDownTime, gKeyEventClassInfo.clazz,
+ "mDownTime", "J");
+ GET_FIELD_ID(gKeyEventClassInfo.mEventTime, gKeyEventClassInfo.clazz,
+ "mEventTime", "J");
+ GET_FIELD_ID(gKeyEventClassInfo.mCharacters, gKeyEventClassInfo.clazz,
+ "mCharacters", "Ljava/lang/String;");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_KeyEvent.h b/core/jni/android_view_KeyEvent.h
new file mode 100644
index 0000000..3c71b1a
--- /dev/null
+++ b/core/jni/android_view_KeyEvent.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_VIEW_KEYEVENT_H
+#define _ANDROID_VIEW_KEYEVENT_H
+
+#include "jni.h"
+
+namespace android {
+
+class KeyEvent;
+
+/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance. */
+extern jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent* event);
+
+/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance. */
+extern void android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature,
+ KeyEvent* event);
+
+} // namespace android
+
+#endif // _ANDROID_OS_KEYEVENT_H
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
new file mode 100644
index 0000000..629c8fe
--- /dev/null
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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 "MotionEvent-JNI"
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <ui/Input.h>
+#include "android_view_MotionEvent.h"
+
+// Number of float items per entry in a DVM sample data array
+#define NUM_SAMPLE_DATA 4
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jclass clazz;
+
+ jmethodID obtain;
+ jmethodID recycle;
+
+ jfieldID mDownTime;
+ jfieldID mEventTimeNano;
+ jfieldID mAction;
+ jfieldID mRawX;
+ jfieldID mRawY;
+ jfieldID mXPrecision;
+ jfieldID mYPrecision;
+ jfieldID mDeviceId;
+ jfieldID mEdgeFlags;
+ jfieldID mMetaState;
+ jfieldID mNumPointers;
+ jfieldID mNumSamples;
+ jfieldID mPointerIdentifiers;
+ jfieldID mDataSamples;
+ jfieldID mTimeSamples;
+} gMotionEventClassInfo;
+
+// ----------------------------------------------------------------------------
+
+jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event) {
+ jint numPointers = jint(event->getPointerCount());
+ jint numHistoricalSamples = jint(event->getHistorySize());
+ jint numSamples = numHistoricalSamples + 1;
+
+ jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
+ gMotionEventClassInfo.obtain, numPointers, numSamples);
+ if (env->ExceptionCheck()) {
+ LOGE("An exception occurred while obtaining a motion event.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ return NULL;
+ }
+
+ // MotionEvent.mEventTimeNano is the time of the oldest sample because
+ // MotionEvent.addBatch does not update it as successive samples are added.
+ jlong eventTimeNano = numHistoricalSamples != 0
+ ? event->getHistoricalEventTime(0)
+ : event->getEventTime();
+
+ env->SetLongField(eventObj, gMotionEventClassInfo.mDownTime,
+ nanoseconds_to_milliseconds(event->getDownTime()));
+ env->SetLongField(eventObj, gMotionEventClassInfo.mEventTimeNano,
+ eventTimeNano);
+ env->SetIntField(eventObj, gMotionEventClassInfo.mAction,
+ event->getAction());
+ env->SetFloatField(eventObj, gMotionEventClassInfo.mRawX,
+ event->getRawX());
+ env->SetFloatField(eventObj, gMotionEventClassInfo.mRawY,
+ event->getRawY());
+ env->SetFloatField(eventObj, gMotionEventClassInfo.mXPrecision,
+ event->getXPrecision());
+ env->SetFloatField(eventObj, gMotionEventClassInfo.mYPrecision,
+ event->getYPrecision());
+ env->SetIntField(eventObj, gMotionEventClassInfo.mDeviceId,
+ event->getDeviceId());
+ env->SetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags,
+ event->getEdgeFlags());
+ env->SetIntField(eventObj, gMotionEventClassInfo.mMetaState,
+ event->getMetaState());
+ env->SetIntField(eventObj, gMotionEventClassInfo.mNumPointers,
+ numPointers);
+ env->SetIntField(eventObj, gMotionEventClassInfo.mNumSamples,
+ numSamples);
+
+ jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj,
+ gMotionEventClassInfo.mPointerIdentifiers));
+ jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
+ gMotionEventClassInfo.mDataSamples));
+ jlongArray timeSampleArray = jlongArray(env->GetObjectField(eventObj,
+ gMotionEventClassInfo.mTimeSamples));
+
+ jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL);
+ jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
+ jlong* timeSamples = (jlong*)env->GetPrimitiveArrayCritical(timeSampleArray, NULL);
+
+ for (jint i = 0; i < numPointers; i++) {
+ pointerIdentifiers[i] = event->getPointerId(i);
+ }
+
+ // Most recent data is in first slot of the DVM array, followed by the oldest,
+ // and then all others are in order.
+
+ jfloat* currentDataSample = dataSamples;
+ jlong* currentTimeSample = timeSamples;
+
+ *(currentTimeSample++) = nanoseconds_to_milliseconds(event->getEventTime());
+ for (jint j = 0; j < numPointers; j++) {
+ *(currentDataSample++) = event->getX(j);
+ *(currentDataSample++) = event->getY(j);
+ *(currentDataSample++) = event->getPressure(j);
+ *(currentDataSample++) = event->getSize(j);
+ }
+
+ for (jint i = 0; i < numHistoricalSamples; i++) {
+ *(currentTimeSample++) = nanoseconds_to_milliseconds(event->getHistoricalEventTime(i));
+ for (jint j = 0; j < numPointers; j++) {
+ *(currentDataSample++) = event->getHistoricalX(j, i);
+ *(currentDataSample++) = event->getHistoricalY(j, i);
+ *(currentDataSample++) = event->getHistoricalPressure(j, i);
+ *(currentDataSample++) = event->getHistoricalSize(j, i);
+ }
+ }
+
+ env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, 0);
+ env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, 0);
+ env->ReleasePrimitiveArrayCritical(timeSampleArray, timeSamples, 0);
+
+ env->DeleteLocalRef(pointerIdentifierArray);
+ env->DeleteLocalRef(dataSampleArray);
+ env->DeleteLocalRef(timeSampleArray);
+ return eventObj;
+}
+
+void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature,
+ MotionEvent* event) {
+ // MotionEvent.mEventTimeNano is the time of the oldest sample because
+ // MotionEvent.addBatch does not update it as successive samples are added.
+ jlong downTime = env->GetLongField(eventObj, gMotionEventClassInfo.mDownTime);
+ jlong eventTimeNano = env->GetLongField(eventObj, gMotionEventClassInfo.mEventTimeNano);
+ jint action = env->GetIntField(eventObj, gMotionEventClassInfo.mAction);
+ jfloat rawX = env->GetFloatField(eventObj, gMotionEventClassInfo.mRawX);
+ jfloat rawY = env->GetFloatField(eventObj, gMotionEventClassInfo.mRawY);
+ jfloat xPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mXPrecision);
+ jfloat yPrecision = env->GetFloatField(eventObj, gMotionEventClassInfo.mYPrecision);
+ jint deviceId = env->GetIntField(eventObj, gMotionEventClassInfo.mDeviceId);
+ jint edgeFlags = env->GetIntField(eventObj, gMotionEventClassInfo.mEdgeFlags);
+ jint metaState = env->GetIntField(eventObj, gMotionEventClassInfo.mMetaState);
+ jint numPointers = env->GetIntField(eventObj, gMotionEventClassInfo.mNumPointers);
+ jint numSamples = env->GetIntField(eventObj, gMotionEventClassInfo.mNumSamples);
+ jintArray pointerIdentifierArray = jintArray(env->GetObjectField(eventObj,
+ gMotionEventClassInfo.mPointerIdentifiers));
+ jfloatArray dataSampleArray = jfloatArray(env->GetObjectField(eventObj,
+ gMotionEventClassInfo.mDataSamples));
+ jlongArray timeSampleArray = jlongArray(env->GetObjectField(eventObj,
+ gMotionEventClassInfo.mTimeSamples));
+
+ LOG_FATAL_IF(numPointers == 0, "numPointers was zero");
+ LOG_FATAL_IF(numSamples == 0, "numSamples was zero");
+
+ jint* pointerIdentifiers = (jint*)env->GetPrimitiveArrayCritical(pointerIdentifierArray, NULL);
+ jfloat* dataSamples = (jfloat*)env->GetPrimitiveArrayCritical(dataSampleArray, NULL);
+ jlong* timeSamples = (jlong*)env->GetPrimitiveArrayCritical(timeSampleArray, NULL);
+
+ // Most recent data is in first slot of the DVM array, followed by the oldest,
+ // and then all others are in order. eventTimeNano is the time of the oldest sample
+ // since MotionEvent.addBatch does not update it.
+
+ jint numHistoricalSamples = numSamples - 1;
+ jint dataSampleStride = numPointers * NUM_SAMPLE_DATA;
+
+ const jfloat* currentDataSample;
+ const jlong* currentTimeSample;
+ if (numHistoricalSamples == 0) {
+ currentDataSample = dataSamples;
+ currentTimeSample = timeSamples;
+ } else {
+ currentDataSample = dataSamples + dataSampleStride;
+ currentTimeSample = timeSamples + 1;
+ }
+
+ PointerCoords pointerCoords[MAX_POINTERS];
+ for (jint j = 0; j < numPointers; j++) {
+ pointerCoords[j].x = *(currentDataSample++);
+ pointerCoords[j].y = *(currentDataSample++);
+ pointerCoords[j].pressure = *(currentDataSample++);
+ pointerCoords[j].size = *(currentDataSample++);
+ }
+
+ event->initialize(deviceId, nature, action, edgeFlags, metaState,
+ rawX, rawY, xPrecision, yPrecision,
+ milliseconds_to_nanoseconds(downTime), eventTimeNano,
+ numPointers, pointerIdentifiers, pointerCoords);
+
+ while (numHistoricalSamples > 0) {
+ numHistoricalSamples -= 1;
+ if (numHistoricalSamples == 0) {
+ currentDataSample = dataSamples;
+ currentTimeSample = timeSamples;
+ }
+
+ nsecs_t sampleEventTime = milliseconds_to_nanoseconds(*(currentTimeSample++));
+
+ for (jint j = 0; j < numPointers; j++) {
+ pointerCoords[j].x = *(currentDataSample++);
+ pointerCoords[j].y = *(currentDataSample++);
+ pointerCoords[j].pressure = *(currentDataSample++);
+ pointerCoords[j].size = *(currentDataSample++);
+ }
+
+ event->addSample(sampleEventTime, pointerCoords);
+ }
+
+ env->ReleasePrimitiveArrayCritical(pointerIdentifierArray, pointerIdentifiers, JNI_ABORT);
+ env->ReleasePrimitiveArrayCritical(dataSampleArray, dataSamples, JNI_ABORT);
+ env->ReleasePrimitiveArrayCritical(timeSampleArray, timeSamples, JNI_ABORT);
+
+ env->DeleteLocalRef(pointerIdentifierArray);
+ env->DeleteLocalRef(dataSampleArray);
+ env->DeleteLocalRef(timeSampleArray);
+}
+
+void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
+ env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle);
+ if (env->ExceptionCheck()) {
+ LOGW("An exception occurred while recycling a motion event.");
+ LOGW_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+#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, fieldDescriptor) \
+ var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find static method" methodName);
+
+#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+ var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method" methodName);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_view_MotionEvent(JNIEnv* env) {
+ FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent");
+
+ GET_STATIC_METHOD_ID(gMotionEventClassInfo.obtain, gMotionEventClassInfo.clazz,
+ "obtain", "(II)Landroid/view/MotionEvent;");
+ GET_METHOD_ID(gMotionEventClassInfo.recycle, gMotionEventClassInfo.clazz,
+ "recycle", "()V");
+
+ GET_FIELD_ID(gMotionEventClassInfo.mDownTime, gMotionEventClassInfo.clazz,
+ "mDownTime", "J");
+ GET_FIELD_ID(gMotionEventClassInfo.mEventTimeNano, gMotionEventClassInfo.clazz,
+ "mEventTimeNano", "J");
+ GET_FIELD_ID(gMotionEventClassInfo.mAction, gMotionEventClassInfo.clazz,
+ "mAction", "I");
+ GET_FIELD_ID(gMotionEventClassInfo.mRawX, gMotionEventClassInfo.clazz,
+ "mRawX", "F");
+ GET_FIELD_ID(gMotionEventClassInfo.mRawY, gMotionEventClassInfo.clazz,
+ "mRawY", "F");
+ GET_FIELD_ID(gMotionEventClassInfo.mXPrecision, gMotionEventClassInfo.clazz,
+ "mXPrecision", "F");
+ GET_FIELD_ID(gMotionEventClassInfo.mYPrecision, gMotionEventClassInfo.clazz,
+ "mYPrecision", "F");
+ GET_FIELD_ID(gMotionEventClassInfo.mDeviceId, gMotionEventClassInfo.clazz,
+ "mDeviceId", "I");
+ GET_FIELD_ID(gMotionEventClassInfo.mEdgeFlags, gMotionEventClassInfo.clazz,
+ "mEdgeFlags", "I");
+ GET_FIELD_ID(gMotionEventClassInfo.mMetaState, gMotionEventClassInfo.clazz,
+ "mMetaState", "I");
+ GET_FIELD_ID(gMotionEventClassInfo.mNumPointers, gMotionEventClassInfo.clazz,
+ "mNumPointers", "I");
+ GET_FIELD_ID(gMotionEventClassInfo.mNumSamples, gMotionEventClassInfo.clazz,
+ "mNumSamples", "I");
+ GET_FIELD_ID(gMotionEventClassInfo.mPointerIdentifiers, gMotionEventClassInfo.clazz,
+ "mPointerIdentifiers", "[I");
+ GET_FIELD_ID(gMotionEventClassInfo.mDataSamples, gMotionEventClassInfo.clazz,
+ "mDataSamples", "[F");
+ GET_FIELD_ID(gMotionEventClassInfo.mTimeSamples, gMotionEventClassInfo.clazz,
+ "mTimeSamples", "[J");
+
+ return 0;
+}
+
+} // namespace android
diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h
new file mode 100644
index 0000000..03ee32f
--- /dev/null
+++ b/core/jni/android_view_MotionEvent.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_VIEW_MOTIONEVENT_H
+#define _ANDROID_VIEW_MOTIONEVENT_H
+
+#include "jni.h"
+
+namespace android {
+
+class MotionEvent;
+
+/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance. */
+extern jobject android_view_MotionEvent_fromNative(JNIEnv* env, const MotionEvent* event);
+
+/* Copies the contents of a DVM MotionEvent object to a native MotionEvent instance. */
+extern void android_view_MotionEvent_toNative(JNIEnv* env, jobject eventObj, int32_t nature,
+ MotionEvent* event);
+
+/* Recycles a DVM MotionEvent object. */
+extern void android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj);
+
+} // namespace android
+
+#endif // _ANDROID_OS_KEYEVENT_H
diff --git a/core/jni/android_view_ViewRoot.cpp b/core/jni/android_view_ViewRoot.cpp
index 70ad8c5..5173bb8 100644
--- a/core/jni/android_view_ViewRoot.cpp
+++ b/core/jni/android_view_ViewRoot.cpp
@@ -80,38 +80,6 @@ static void android_view_ViewRoot_abandonGlCaches(JNIEnv* env, jobject) {
SkGLCanvas::AbandonAllTextures();
}
-static jintArray android_view_ViewRoot_makeInputChannel(JNIEnv* env, jobject) {
- int fd[2];
- jint* arrayData = NULL;
-
- // Create the pipe
- int err = socketpair(AF_LOCAL, SOCK_STREAM, 0, fd);
- if (err != 0) {
- fprintf(stderr, "socketpair() failed: %d\n", errno);
- doThrow(env, "java/lang/RuntimeException", "Unable to create pipe");
- return NULL;
- }
-
- // Set up the return array
- jintArray array = env->NewIntArray(2);
- if (env->ExceptionCheck()) {
- fprintf(stderr, "Exception allocating fd array");
- goto bail;
- }
-
- arrayData = env->GetIntArrayElements(array, 0);
- arrayData[0] = fd[0];
- arrayData[1] = fd[1];
- env->ReleaseIntArrayElements(array, arrayData, 0);
-
- return array;
-
-bail:
- env->DeleteLocalRef(array);
- close(fd[0]);
- close(fd[1]);
- return NULL;
-}
// ----------------------------------------------------------------------------
@@ -121,9 +89,7 @@ static JNINativeMethod gMethods[] = {
{ "nativeShowFPS", "(Landroid/graphics/Canvas;I)V",
(void*)android_view_ViewRoot_showFPS },
{ "nativeAbandonGlCaches", "()V",
- (void*)android_view_ViewRoot_abandonGlCaches },
- { "makeInputChannel", "()[I",
- (void*)android_view_ViewRoot_makeInputChannel }
+ (void*)android_view_ViewRoot_abandonGlCaches }
};
int register_android_view_ViewRoot(JNIEnv* env) {