Native input dispatch rewrite work in progress.

The old dispatch mechanism has been left in place and continues to
be used by default for now.  To enable native input dispatch,
edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy.

Includes part of the new input event NDK API.  Some details TBD.

To wire up input dispatch, as the ViewRoot adds a window to the
window session it receives an InputChannel object as an output
argument.  The InputChannel encapsulates the file descriptors for a
shared memory region and two pipe end-points.  The ViewRoot then
provides the InputChannel to the InputQueue.  Behind the
scenes, InputQueue simply attaches handlers to the native PollLoop object
that underlies the MessageQueue.  This way MessageQueue doesn't need
to know anything about input dispatch per-se, it just exposes (in native
code) a PollLoop that other components can use to monitor file descriptor
state changes.

There can be zero or more targets for any given input event.  Each
input target is specified by its input channel and some parameters
including flags, an X/Y coordinate offset, and the dispatch timeout.
An input target can request either synchronous dispatch (for foreground apps)
or asynchronous dispatch (fire-and-forget for wallpapers and "outside"
targets).  Currently, finding the appropriate input targets for an event
requires a call back into the WindowManagerServer from native code.
In the future this will be refactored to avoid most of these callbacks
except as required to handle pending focus transitions.

End-to-end event dispatch mostly works!

To do: event injection, rate limiting, ANRs, testing, optimization, etc.

Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25
This commit is contained in:
Jeff Brown
2010-04-22 18:58:52 -07:00
parent f62c57d684
commit 46b9ac0ae2
75 changed files with 12279 additions and 791 deletions

View File

@@ -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();
}
}
}

View File

@@ -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) {
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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.
*

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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 \

View File

@@ -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),
};
/*

View File

@@ -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>
using 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;
// ----------------------------------------------------------------------------
static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
{
if (jniThrowException(env, exc, msg) != 0)
assert(false);
}
// ----------------------------------------------------------------------------
class MessageQueueNative {
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);
// signal the queue-ready pipe
void signalQueuePipe();
// 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(); }
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;
};
MessageQueueNative::MessageQueueNative(int readSocket, int writeSocket)
: mQueueReadFd(readSocket), mQueueWriteFd(writeSocket) {
}
MessageQueueNative::~MessageQueueNative() {
}
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;
}
// 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);
}
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.
}
// 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();
}
// 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;
}
// ----------------------------------------------------------------------------
#include "android_os_MessageQueue.h"
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");
}
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");
}
// ----------------------------------------------------------------------------
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);
static struct {
jclass clazz;
env->SetIntField(obj, gMessageQueueOffsets.mObject, (jint)mqn);
jfieldID mPtr; // native object attached to the DVM MessageQueue
} gMessageQueueClassInfo;
// ----------------------------------------------------------------------------
class NativeMessageQueue {
public:
NativeMessageQueue();
~NativeMessageQueue();
inline sp<PollLoop> getPollLoop() { return mPollLoop; }
bool pollOnce(int timeoutMillis);
void wake();
private:
sp<PollLoop> mPollLoop;
};
// ----------------------------------------------------------------------------
NativeMessageQueue::NativeMessageQueue() {
mPollLoop = new PollLoop();
}
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");
}
NativeMessageQueue::~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;
bool NativeMessageQueue::pollOnce(int timeoutMillis) {
return mPollLoop->pollOnce(timeoutMillis);
}
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 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");
}
void NativeMessageQueue::wake() {
mPollLoop->wake();
}
// ----------------------------------------------------------------------------
const char* const kKeyEventPathName = "android/view/KeyEvent";
const char* const kMessagePathName = "android/os/Message";
const char* const kMessageQueuePathName = "android/os/MessageQueue";
static NativeMessageQueue* android_os_MessageQueue_getNativeMessageQueue(JNIEnv* env,
jobject messageQueueObj) {
jint intPtr = env->GetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr);
return reinterpret_cast<NativeMessageQueue*>(intPtr);
}
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
NativeMessageQueue* nativeMessageQueue) {
env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
reinterpret_cast<jint>(nativeMessageQueue));
}
sp<PollLoop> android_os_MessageQueue_getPollLoop(JNIEnv* env, jobject messageQueueObj) {
NativeMessageQueue* nativeMessageQueue =
android_os_MessageQueue_getNativeMessageQueue(env, messageQueueObj);
return nativeMessageQueue != NULL ? nativeMessageQueue->getPollLoop() : NULL;
}
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (! nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return;
}
android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
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 void throwQueueNotInitialized(JNIEnv* env) {
jniThrowException(env, "java/lang/IllegalStateException", "Message 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_nativeWake(JNIEnv* env, jobject obj) {
NativeMessageQueue* nativeMessageQueue =
android_os_MessageQueue_getNativeMessageQueue(env, obj);
if (! nativeMessageQueue) {
throwQueueNotInitialized(env);
return;
}
return nativeMessageQueue->wake();
}
// ----------------------------------------------------------------------------
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;
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);
return AndroidRuntime::registerNativeMethods(env, kMessageQueuePathName,
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");
GET_FIELD_ID(gMessageQueueClassInfo.mPtr, gMessageQueueClassInfo.clazz,
"mPtr", "I");
return 0;
}
}; // end of namespace android
} // namespace android

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

4
include/private/README Normal file
View File

@@ -0,0 +1,4 @@
This folder contains private include files.
These include files are part of the private implementation details of
various framework components. Use at your peril.

View File

@@ -18,6 +18,7 @@
#ifndef _RUNTIME_EVENT_HUB_H
#define _RUNTIME_EVENT_HUB_H
#include <android/input.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Log.h>
@@ -27,6 +28,31 @@
#include <linux/input.h>
/* These constants are not defined in linux/input.h but they are part of the multitouch
* input protocol. */
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */
#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device (finger, pen, ...) */
#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
#define MT_TOOL_FINGER 0 /* Identifies a finger */
#define MT_TOOL_PEN 1 /* Identifies a pen */
#define SYN_MT_REPORT 2
/* Convenience constants. */
#define BTN_FIRST 0x100 // first button scancode
#define BTN_LAST 0x15f // last button scancode
struct pollfd;
namespace android {
@@ -34,62 +60,101 @@ namespace android {
class KeyLayoutMap;
/*
* Grand Central Station for events. With a single call to waitEvent()
* you can wait for:
* - input events from the keypad of a real device
* - input events and meta-events (e.g. "quit") from the simulator
* - synthetic events from the runtime (e.g. "URL fetch completed")
* - real or forged "vsync" events
* Grand Central Station for events.
*
* Do not instantiate this class. Instead, call startUp().
* The event hub aggregates input events received across all known input
* devices on the system, including devices that may be emulated by the simulator
* environment. In addition, the event hub generates fake input events to indicate
* when devices are added or removed.
*
* The event hub provies a stream of input events (via the getEvent function).
* It also supports querying the current actual state of input devices such as identifying
* which keys are currently down. Finally, the event hub keeps track of the capabilities of
* individual input devices, such as their class and the set of key codes that they support.
*/
class EventHub : public RefBase
{
class EventHubInterface : public virtual RefBase {
protected:
EventHubInterface() { }
virtual ~EventHubInterface() { }
public:
EventHub();
status_t errorCheck() const;
// bit fields for classes of devices.
enum {
CLASS_KEYBOARD = 0x00000001,
CLASS_ALPHAKEY = 0x00000002,
CLASS_TOUCHSCREEN = 0x00000004,
CLASS_TRACKBALL = 0x00000008,
CLASS_TOUCHSCREEN_MT= 0x00000010,
CLASS_DPAD = 0x00000020
};
uint32_t getDeviceClasses(int32_t deviceId) const;
String8 getDeviceName(int32_t deviceId) const;
int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
int* outMaxValue, int* outFlat, int* outFuzz) const;
int getSwitchState(int sw) const;
int getSwitchState(int32_t deviceId, int sw) const;
int getScancodeState(int key) const;
int getScancodeState(int32_t deviceId, int key) const;
int getKeycodeState(int key) const;
int getKeycodeState(int32_t deviceId, int key) const;
status_t scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const;
// exclude a particular device from opening
// this can be used to ignore input devices for sensors
void addExcludedDevice(const char* deviceName);
// special type codes when devices are added/removed.
// Synthetic raw event type codes produced when devices are added or removed.
enum {
DEVICE_ADDED = 0x10000000,
DEVICE_REMOVED = 0x20000000
};
virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0;
virtual String8 getDeviceName(int32_t deviceId) const = 0;
virtual int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
int* outMaxValue, int* outFlat, int* outFuzz) const = 0;
virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const = 0;
// exclude a particular device from opening
// this can be used to ignore input devices for sensors
virtual void addExcludedDevice(const char* deviceName) = 0;
/*
* Wait for the next event to become available and return it.
* After returning, the EventHub holds onto a wake lock until the next call to getEvent.
* This ensures that the device will not go to sleep while the event is being processed.
* If the device needs to remain awake longer than that, then the caller is responsible
* for taking care of it (say, by poking the power manager user activity timer).
*/
virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen) = 0;
/*
* Query current input state.
* deviceId may be -1 to search for the device automatically, filtered by class.
* deviceClasses may be -1 to ignore device class while searching.
*/
virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t scanCode) const = 0;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t keyCode) const = 0;
virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
int32_t sw) const = 0;
/*
* Examine key input devices for specific framework keycode support
*/
virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes,
uint8_t* outFlags) const = 0;
};
class EventHub : public EventHubInterface
{
public:
EventHub();
status_t errorCheck() const;
virtual uint32_t getDeviceClasses(int32_t deviceId) const;
// examine key input devices for specific framework keycode support
bool hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags);
virtual String8 getDeviceName(int32_t deviceId) const;
virtual int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
int* outMaxValue, int* outFlat, int* outFuzz) const;
virtual status_t scancodeToKeycode(int32_t deviceId, int scancode,
int32_t* outKeycode, uint32_t* outFlags) const;
virtual void addExcludedDevice(const char* deviceName);
virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t scanCode) const;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t keyCode) const;
virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
int32_t sw) const;
virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
@@ -126,6 +191,10 @@ private:
device_t* getDevice(int32_t deviceId) const;
bool hasKeycode(device_t* device, int keycode) const;
int32_t getScanCodeStateLocked(device_t* device, int32_t scanCode) const;
int32_t getKeyCodeStateLocked(device_t* device, int32_t keyCode) const;
int32_t getSwitchStateLocked(device_t* device, int32_t sw) const;
// Protect all internal state.
mutable Mutex mLock;
@@ -151,7 +220,7 @@ private:
// device ids that report particular switches.
#ifdef EV_SW
int32_t mSwitches[SW_MAX+1];
int32_t mSwitches[SW_MAX + 1];
#endif
};

310
include/ui/Input.h Normal file
View File

@@ -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.
*/
#ifndef _UI_INPUT_H
#define _UI_INPUT_H
/**
* Native input event structures.
*/
#include <android/input.h>
#include <utils/Vector.h>
#include <utils/Timers.h>
/*
* Additional private constants not defined in ndk/ui/input.h.
*/
enum {
/*
* Private control to determine when an app is tracking a key sequence.
*/
KEY_EVENT_FLAG_START_TRACKING = 0x40000000
};
/*
* Maximum number of pointers supported per motion event.
*/
#define MAX_POINTERS 10
namespace android {
/*
* A raw event as retrieved from the EventHub.
*/
struct RawEvent {
nsecs_t when;
int32_t deviceId;
int32_t type;
int32_t scanCode;
int32_t keyCode;
int32_t value;
uint32_t flags;
};
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
*
* TODO This enumeration should probably be split up or relabeled for clarity.
*/
enum {
/* These flags originate in RawEvents and are generally set in the key map. */
POLICY_FLAG_WAKE = 0x00000001,
POLICY_FLAG_WAKE_DROPPED = 0x00000002,
POLICY_FLAG_SHIFT = 0x00000004,
POLICY_FLAG_CAPS_LOCK = 0x00000008,
POLICY_FLAG_ALT = 0x00000010,
POLICY_FLAG_ALT_GR = 0x00000020,
POLICY_FLAG_MENU = 0x00000040,
POLICY_FLAG_LAUNCHER = 0x00000080,
/* These flags are set by the input dispatch policy as it intercepts each event. */
// Indicates that the screen was off when the event was received and the event
// should wake the device.
POLICY_FLAG_WOKE_HERE = 0x10000000,
// Indicates that the screen was dim when the event was received and the event
// should brighten the device.
POLICY_FLAG_BRIGHT_HERE = 0x20000000,
};
/*
* Pointer coordinate data.
*/
struct PointerCoords {
float x;
float y;
float pressure;
float size;
};
/*
* Input events.
*/
struct input_event_t { };
class InputEvent : public input_event_t {
public:
virtual ~InputEvent() { }
virtual int32_t getType() const = 0;
inline int32_t getDeviceId() const { return mDeviceId; }
inline int32_t getNature() const { return mNature; }
protected:
void initialize(int32_t deviceId, int32_t nature);
private:
int32_t mDeviceId;
int32_t mNature;
};
class KeyEvent : public InputEvent {
public:
virtual ~KeyEvent() { }
virtual int32_t getType() const { return INPUT_EVENT_TYPE_KEY; }
inline int32_t getAction() const { return mAction; }
inline int32_t getFlags() const { return mFlags; }
inline int32_t getKeyCode() const { return mKeyCode; }
inline int32_t getScanCode() const { return mScanCode; }
inline int32_t getMetaState() const { return mMetaState; }
inline int32_t getRepeatCount() const { return mRepeatCount; }
inline nsecs_t getDownTime() const { return mDownTime; }
inline nsecs_t getEventTime() const { return mEventTime; }
void initialize(
int32_t deviceId,
int32_t nature,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime);
private:
int32_t mAction;
int32_t mFlags;
int32_t mKeyCode;
int32_t mScanCode;
int32_t mMetaState;
int32_t mRepeatCount;
nsecs_t mDownTime;
nsecs_t mEventTime;
};
class MotionEvent : public InputEvent {
public:
virtual ~MotionEvent() { }
virtual int32_t getType() const { return INPUT_EVENT_TYPE_MOTION; }
inline int32_t getAction() const { return mAction; }
inline int32_t getEdgeFlags() const { return mEdgeFlags; }
inline int32_t getMetaState() const { return mMetaState; }
inline float getXPrecision() const { return mXPrecision; }
inline float getYPrecision() const { return mYPrecision; }
inline nsecs_t getDownTime() const { return mDownTime; }
inline size_t getPointerCount() const { return mPointerIds.size(); }
inline int32_t getPointerId(size_t pointerIndex) const { return mPointerIds[pointerIndex]; }
inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; }
inline float getRawX() const { return mRawX; }
inline float getRawY() const { return mRawY; }
inline float getX(size_t pointerIndex) const {
return getCurrentPointerCoords(pointerIndex).x;
}
inline float getY(size_t pointerIndex) const {
return getCurrentPointerCoords(pointerIndex).y;
}
inline float getPressure(size_t pointerIndex) const {
return getCurrentPointerCoords(pointerIndex).pressure;
}
inline float getSize(size_t pointerIndex) const {
return getCurrentPointerCoords(pointerIndex).size;
}
inline size_t getHistorySize() const { return mSampleEventTimes.size() - 1; }
inline nsecs_t getHistoricalEventTime(size_t historicalIndex) const {
return mSampleEventTimes[historicalIndex];
}
inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalPointerCoords(pointerIndex, historicalIndex).x;
}
inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalPointerCoords(pointerIndex, historicalIndex).y;
}
inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalPointerCoords(pointerIndex, historicalIndex).pressure;
}
inline float getHistoricalSize(size_t pointerIndex, size_t historicalIndex) const {
return getHistoricalPointerCoords(pointerIndex, historicalIndex).size;
}
void initialize(
int32_t deviceId,
int32_t nature,
int32_t action,
int32_t edgeFlags,
int32_t metaState,
float rawX,
float rawY,
float xPrecision,
float yPrecision,
nsecs_t downTime,
nsecs_t eventTime,
size_t pointerCount,
const int32_t* pointerIds,
const PointerCoords* pointerCoords);
void addSample(
nsecs_t eventTime,
const PointerCoords* pointerCoords);
void offsetLocation(float xOffset, float yOffset);
private:
int32_t mAction;
int32_t mEdgeFlags;
int32_t mMetaState;
float mRawX;
float mRawY;
float mXPrecision;
float mYPrecision;
nsecs_t mDownTime;
Vector<int32_t> mPointerIds;
Vector<nsecs_t> mSampleEventTimes;
Vector<PointerCoords> mSamplePointerCoords;
inline const PointerCoords& getCurrentPointerCoords(size_t pointerIndex) const {
return mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
}
inline const PointerCoords& getHistoricalPointerCoords(
size_t pointerIndex, size_t historicalIndex) const {
return mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex];
}
};
/*
* Input event factory.
*/
class InputEventFactoryInterface {
protected:
virtual ~InputEventFactoryInterface() { }
public:
InputEventFactoryInterface() { }
virtual KeyEvent* createKeyEvent() = 0;
virtual MotionEvent* createMotionEvent() = 0;
};
/*
* A simple input event factory implementation that uses a single preallocated instance
* of each type of input event that are reused for each request.
*/
class PreallocatedInputEventFactory : public InputEventFactoryInterface {
public:
PreallocatedInputEventFactory() { }
virtual ~PreallocatedInputEventFactory() { }
virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
private:
KeyEvent mKeyEvent;
MotionEvent mMotionEvent;
};
} // namespace android
#endif // _UI_INPUT_H

View File

@@ -0,0 +1,198 @@
/*
* 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 _UI_INPUT_DISPATCH_POLICY_H
#define _UI_INPUT_DISPATCH_POLICY_H
/**
* Native input dispatch policy.
*/
#include <ui/Input.h>
#include <utils/Errors.h>
#include <utils/Vector.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
namespace android {
class InputChannel;
/*
* 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.
*/
struct InputTarget {
enum {
/* This flag indicates that subsequent event delivery should be held until the
* current event is delivered to this target or a timeout occurs. */
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. */
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. */
FLAG_CANCEL = 0x04
};
// The input channel to be targeted.
sp<InputChannel> inputChannel;
// Flags for the input target.
int32_t flags;
// The timeout for event delivery to this target in nanoseconds. Or -1 if none.
nsecs_t timeout;
// The x and y offset to add to a MotionEvent as it is delivered.
// (ignored for KeyEvents)
float xOffset, yOffset;
};
/*
* Input dispatch policy interface.
*
* The input dispatch policy is used by the input dispatcher to interact with the
* Window Manager and other system components. This separation of concerns keeps
* the input dispatcher relatively free of special case logic such as is required
* to determine the target of iput events, when to wake the device, how to interact
* with key guard, and when to transition to the home screen.
*
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This class is also mocked in the input dispatcher unit tests since
* it is an ideal test seam.
*/
class InputDispatchPolicyInterface : public virtual RefBase {
protected:
InputDispatchPolicyInterface() { }
virtual ~InputDispatchPolicyInterface() { }
public:
enum {
ROTATION_0 = 0,
ROTATION_90 = 1,
ROTATION_180 = 2,
ROTATION_270 = 3
};
enum {
// The input dispatcher should do nothing and discard the input unless other
// flags are set.
ACTION_NONE = 0,
// The input dispatcher should dispatch the input to the application.
ACTION_DISPATCH = 0x00000001,
// The input dispatcher should perform special filtering in preparation for
// a pending app switch.
ACTION_APP_SWITCH_COMING = 0x00000002,
// The input dispatcher should add POLICY_FLAG_WOKE_HERE to the policy flags it
// passes through the dispatch pipeline.
ACTION_WOKE_HERE = 0x00000004,
// The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
// passes through the dispatch pipeline.
ACTION_BRIGHT_HERE = 0x00000008
};
enum {
TOUCHSCREEN_UNDEFINED = 0,
TOUCHSCREEN_NOTOUCH = 1,
TOUCHSCREEN_STYLUS = 2,
TOUCHSCREEN_FINGER = 3
};
enum {
KEYBOARD_UNDEFINED = 0,
KEYBOARD_NOKEYS = 1,
KEYBOARD_QWERTY = 2,
KEYBOARD_12KEY = 3
};
enum {
NAVIGATION_UNDEFINED = 0,
NAVIGATION_NONAV = 1,
NAVIGATION_DPAD = 2,
NAVIGATION_TRACKBALL = 3,
NAVIGATION_WHEEL = 4
};
struct VirtualKeyDefinition {
int32_t scanCode;
// configured position data, specified in display coords
int32_t centerX;
int32_t centerY;
int32_t width;
int32_t height;
};
/* Gets information about the display with the specified id.
* Returns true if the display info is available, false otherwise.
*/
virtual bool getDisplayInfo(int32_t displayId,
int32_t* width, int32_t* height, int32_t* orientation) = 0;
virtual void notifyConfigurationChanged(nsecs_t when,
int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) = 0;
virtual void notifyLidSwitchChanged(nsecs_t when, bool lidOpen) = 0;
virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) = 0;
virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
bool rolled) = 0;
virtual int32_t interceptTouch(nsecs_t when) = 0;
virtual bool allowKeyRepeat() = 0;
virtual nsecs_t getKeyRepeatTimeout() = 0;
virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
Vector<InputTarget>& outTargets) = 0;
virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
Vector<InputTarget>& outTargets) = 0;
/* Determine whether to turn on some hacks we have to improve the touch interaction with a
* certain device whose screen currently is not all that good.
*/
virtual bool filterTouchEvents() = 0;
/* Determine whether to turn on some hacks to improve touch interaction with another device
* where touch coordinate data can get corrupted.
*/
virtual bool filterJumpyTouchEvents() = 0;
virtual void getVirtualKeyDefinitions(const String8& deviceName,
Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0;
virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0;
};
} // namespace android
#endif // _UI_INPUT_DISPATCH_POLICY_H

View File

@@ -0,0 +1,409 @@
/*
* 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 _UI_INPUT_DISPATCHER_H
#define _UI_INPUT_DISPATCHER_H
#include <ui/Input.h>
#include <ui/InputDispatchPolicy.h>
#include <ui/InputTransport.h>
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
#include <utils/threads.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/PollLoop.h>
#include <utils/Pool.h>
#include <stddef.h>
#include <unistd.h>
namespace android {
/* Notifies the system about input events generated by the input reader.
* The dispatcher is expected to be mostly asynchronous. */
class InputDispatcherInterface : public virtual RefBase {
protected:
InputDispatcherInterface() { }
virtual ~InputDispatcherInterface() { }
public:
/* Runs a single iteration of the dispatch loop.
* Nominally processes one queued event, a timeout, or a response from an input consumer.
*
* This method should only be called on the input dispatcher thread.
*/
virtual void dispatchOnce() = 0;
/* Notifies the dispatcher about new events.
* The dispatcher will process most of these events asynchronously although some
* policy processing may occur synchronously.
*
* These methods should only be called on the input reader thread.
*/
virtual void notifyConfigurationChanged(nsecs_t eventTime,
int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) = 0;
virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen) = 0;
virtual void notifyAppSwitchComing(nsecs_t eventTime) = 0;
virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nature,
uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0;
virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t nature,
uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags,
uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
float xPrecision, float yPrecision, nsecs_t downTime) = 0;
/* Registers or unregister input channels that may be used as targets for input events.
*
* These methods may be called on any thread (usually by the input manager).
*/
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
};
/* Dispatches events. */
class InputDispatcher : public InputDispatcherInterface {
protected:
virtual ~InputDispatcher();
public:
explicit InputDispatcher(const sp<InputDispatchPolicyInterface>& policy);
virtual void dispatchOnce();
virtual void notifyConfigurationChanged(nsecs_t eventTime,
int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig);
virtual void notifyLidSwitchChanged(nsecs_t eventTime, bool lidOpen);
virtual void notifyAppSwitchComing(nsecs_t eventTime);
virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nature,
uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime);
virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t nature,
uint32_t policyFlags, int32_t action, int32_t metaState, int32_t edgeFlags,
uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
float xPrecision, float yPrecision, nsecs_t downTime);
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
private:
template <typename T>
struct Link {
T* next;
T* prev;
};
struct EventEntry : Link<EventEntry> {
enum {
TYPE_SENTINEL,
TYPE_CONFIGURATION_CHANGED,
TYPE_KEY,
TYPE_MOTION
};
int32_t refCount;
int32_t type;
nsecs_t eventTime;
};
struct ConfigurationChangedEntry : EventEntry {
int32_t touchScreenConfig;
int32_t keyboardConfig;
int32_t navigationConfig;
};
struct KeyEntry : EventEntry {
int32_t deviceId;
int32_t nature;
uint32_t policyFlags;
int32_t action;
int32_t flags;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
int32_t repeatCount;
nsecs_t downTime;
};
struct MotionSample {
MotionSample* next;
nsecs_t eventTime;
PointerCoords pointerCoords[MAX_POINTERS];
};
struct MotionEntry : EventEntry {
int32_t deviceId;
int32_t nature;
uint32_t policyFlags;
int32_t action;
int32_t metaState;
int32_t edgeFlags;
float xPrecision;
float yPrecision;
nsecs_t downTime;
uint32_t pointerCount;
int32_t pointerIds[MAX_POINTERS];
// Linked list of motion samples associated with this motion event.
MotionSample firstSample;
MotionSample* lastSample;
};
struct DispatchEntry : Link<DispatchEntry> {
EventEntry* eventEntry; // the event to dispatch
int32_t targetFlags;
float xOffset;
float yOffset;
nsecs_t timeout;
// True if dispatch has started.
bool inProgress;
// For motion events:
// Pointer to the first motion sample to dispatch in this cycle.
// Usually NULL to indicate that the list of motion samples begins at
// MotionEntry::firstSample. Otherwise, some samples were dispatched in a previous
// cycle and this pointer indicates the location of the first remainining sample
// to dispatch during the current cycle.
MotionSample* headMotionSample;
// Pointer to a motion sample to dispatch in the next cycle if the dispatcher was
// unable to send all motion samples during this cycle. On the next cycle,
// headMotionSample will be initialized to tailMotionSample and tailMotionSample
// will be set to NULL.
MotionSample* tailMotionSample;
};
template <typename T>
struct Queue {
T head;
T tail;
inline Queue() {
head.prev = NULL;
head.next = & tail;
tail.prev = & head;
tail.next = NULL;
}
inline bool isEmpty() {
return head.next == & tail;
}
inline void enqueueAtTail(T* entry) {
T* last = tail.prev;
last->next = entry;
entry->prev = last;
entry->next = & tail;
tail.prev = entry;
}
inline void enqueueAtHead(T* entry) {
T* first = head.next;
head.next = entry;
entry->prev = & head;
entry->next = first;
first->prev = entry;
}
inline void dequeue(T* entry) {
entry->prev->next = entry->next;
entry->next->prev = entry->prev;
}
inline T* dequeueAtHead() {
T* first = head.next;
dequeue(first);
return first;
}
};
/* Allocates queue entries and performs reference counting as needed. */
class Allocator {
public:
Allocator();
ConfigurationChangedEntry* obtainConfigurationChangedEntry();
KeyEntry* obtainKeyEntry();
MotionEntry* obtainMotionEntry();
DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
void releaseEventEntry(EventEntry* entry);
void releaseConfigurationChangedEntry(ConfigurationChangedEntry* entry);
void releaseKeyEntry(KeyEntry* entry);
void releaseMotionEntry(MotionEntry* entry);
void releaseDispatchEntry(DispatchEntry* entry);
void appendMotionSample(MotionEntry* motionEntry,
nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords);
void freeMotionSample(MotionSample* sample);
void freeMotionSampleList(MotionSample* head);
private:
Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
Pool<KeyEntry> mKeyEntryPool;
Pool<MotionEntry> mMotionEntryPool;
Pool<MotionSample> mMotionSamplePool;
Pool<DispatchEntry> mDispatchEntryPool;
};
/* Manages the dispatch state associated with a single input channel. */
class Connection : public RefBase {
protected:
virtual ~Connection();
public:
enum Status {
// Everything is peachy.
STATUS_NORMAL,
// An unrecoverable communication error has occurred.
STATUS_BROKEN,
// The client is not responding.
STATUS_NOT_RESPONDING,
// The input channel has been unregistered.
STATUS_ZOMBIE
};
Status status;
sp<InputChannel> inputChannel;
InputPublisher inputPublisher;
Queue<DispatchEntry> outboundQueue;
nsecs_t nextTimeoutTime; // next timeout time (LONG_LONG_MAX if none)
nsecs_t lastEventTime; // the time when the event was originally captured
nsecs_t lastDispatchTime; // the time when the last event was dispatched
nsecs_t lastANRTime; // the time when the last ANR was recorded
explicit Connection(const sp<InputChannel>& inputChannel);
inline const char* getInputChannelName() { return inputChannel->getName().string(); }
// Finds a DispatchEntry in the outbound queue associated with the specified event.
// Returns NULL if not found.
DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const;
// Determine whether this connection has a pending synchronous dispatch target.
// Since there can only ever be at most one such target at a time, if there is one,
// it must be at the tail because nothing else can be enqueued after it.
inline bool hasPendingSyncTarget() {
return ! outboundQueue.isEmpty()
&& (outboundQueue.tail.prev->targetFlags & InputTarget::FLAG_SYNC);
}
// Gets the time since the current event was originally obtained from the input driver.
inline double getEventLatencyMillis(nsecs_t currentTime) {
return (currentTime - lastEventTime) / 1000000.0;
}
// Gets the time since the current event entered the outbound dispatch queue.
inline double getDispatchLatencyMillis(nsecs_t currentTime) {
return (currentTime - lastDispatchTime) / 1000000.0;
}
// Gets the time since the current event ANR was declared, if applicable.
inline double getANRLatencyMillis(nsecs_t currentTime) {
return (currentTime - lastANRTime) / 1000000.0;
}
status_t initialize();
};
sp<InputDispatchPolicyInterface> mPolicy;
Mutex mLock;
Queue<EventEntry> mInboundQueue;
Allocator mAllocator;
sp<PollLoop> mPollLoop;
// All registered connections mapped by receive pipe file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
// Active connections are connections that have a non-empty outbound queue.
Vector<Connection*> mActiveConnections;
// Pool of key and motion event objects used only to ask the input dispatch policy
// for the targets of an event that is to be dispatched.
KeyEvent mReusableKeyEvent;
MotionEvent mReusableMotionEvent;
// The input targets that were most recently identified for dispatch.
// If there is a synchronous event dispatch in progress, the current input targets will
// remain unchanged until the dispatch has completed or been aborted.
Vector<InputTarget> mCurrentInputTargets;
// Key repeat tracking.
// XXX Move this up to the input reader instead.
struct KeyRepeatState {
KeyEntry* lastKeyEntry; // or null if no repeat
nsecs_t nextRepeatTime;
} mKeyRepeatState;
void resetKeyRepeatLocked();
// Process events that have just been dequeued from the head of the input queue.
void processConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry);
void processKeyLocked(nsecs_t currentTime, KeyEntry* entry);
void processKeyRepeatLocked(nsecs_t currentTime);
void processMotionLocked(nsecs_t currentTime, MotionEntry* entry);
// Identify input targets for an event and dispatch to them.
void identifyInputTargetsAndDispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry);
void identifyInputTargetsAndDispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry);
void dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, EventEntry* entry,
bool resumeWithAppendedMotionSample);
// Manage the dispatch cycle for a single connection.
void prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample);
void startDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
void finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
bool timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
bool abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
bool broken);
static bool handleReceiveCallback(int receiveFd, int events, void* data);
// Add or remove a connection to the mActiveConnections vector.
void activateConnectionLocked(Connection* connection);
void deactivateConnectionLocked(Connection* connection);
// Interesting events that we might like to log or tell the framework about.
void onDispatchCycleStartedLocked(nsecs_t currentTime, Connection* connection);
void onDispatchCycleFinishedLocked(nsecs_t currentTime, Connection* connection,
bool recoveredFromANR);
void onDispatchCycleANRLocked(nsecs_t currentTime, Connection* connection);
void onDispatchCycleBrokenLocked(nsecs_t currentTime, Connection* connection);
};
/* Enqueues and dispatches input events, endlessly. */
class InputDispatcherThread : public Thread {
public:
explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
~InputDispatcherThread();
private:
virtual bool threadLoop();
sp<InputDispatcherInterface> mDispatcher;
};
} // namespace android
#endif // _UI_INPUT_DISPATCHER_PRIV_H

134
include/ui/InputManager.h Normal file
View File

@@ -0,0 +1,134 @@
/*
* 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 _UI_INPUT_MANAGER_H
#define _UI_INPUT_MANAGER_H
/**
* Native input manager.
*/
#include <ui/EventHub.h>
#include <ui/Input.h>
#include <ui/InputDispatchPolicy.h>
#include <utils/Errors.h>
#include <utils/Vector.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
namespace android {
class InputReader;
class InputDispatcher;
class InputReaderThread;
class InputDispatcherThread;
/*
* The input manager is the core of the system event processing.
*
* The input manager uses two threads.
*
* 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
* applies policy, and posts messages to a queue managed by the DispatcherThread.
* 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
* queue and asynchronously dispatches them to applications.
*
* By design, the InputReaderThread class and InputDispatcherThread class do not share any
* internal state. Moreover, all communication is done one way from the InputReaderThread
* into the InputDispatcherThread and never the reverse. Both classes may interact with the
* InputDispatchPolicy, however.
*
* The InputManager class never makes any calls into Java itself. Instead, the
* InputDispatchPolicy is responsible for performing all external interactions with the
* system, including calling DVM services.
*/
class InputManagerInterface : public virtual RefBase {
protected:
InputManagerInterface() { }
virtual ~InputManagerInterface() { }
public:
/* Starts the input manager threads. */
virtual status_t start() = 0;
/* Stops the input manager threads and waits for them to exit. */
virtual status_t stop() = 0;
/* Registers an input channel prior to using it as the target of an event. */
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
/* Unregisters an input channel. */
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
/*
* Query current input state.
* deviceId may be -1 to search for the device automatically, filtered by class.
* deviceClasses may be -1 to ignore device class while searching.
*/
virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t scanCode) const = 0;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t keyCode) const = 0;
virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
int32_t sw) const = 0;
/* Determine whether physical keys exist for the given framework-domain key codes. */
virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const = 0;
};
class InputManager : public InputManagerInterface {
protected:
virtual ~InputManager();
public:
/*
* Creates an input manager that reads events from the given
* event hub and applies the given input dispatch policy.
*/
InputManager(const sp<EventHubInterface>& eventHub,
const sp<InputDispatchPolicyInterface>& policy);
virtual status_t start();
virtual status_t stop();
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t scanCode) const;
virtual int32_t getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t keyCode) const;
virtual int32_t getSwitchState(int32_t deviceId, int32_t deviceClasses,
int32_t sw) const;
virtual bool hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const;
private:
sp<EventHubInterface> mEventHub;
sp<InputDispatchPolicyInterface> mPolicy;
sp<InputDispatcher> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread;
sp<InputReader> mReader;
sp<InputReaderThread> mReaderThread;
void configureExcludedDevices();
};
} // namespace android
#endif // _UI_INPUT_MANAGER_H

472
include/ui/InputReader.h Normal file
View File

@@ -0,0 +1,472 @@
/*
* 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 _UI_INPUT_READER_H
#define _UI_INPUT_READER_H
#include <ui/EventHub.h>
#include <ui/Input.h>
#include <ui/InputDispatchPolicy.h>
#include <ui/InputDispatcher.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
#include <utils/BitSet.h>
#include <stddef.h>
#include <unistd.h>
/* Maximum pointer id value supported.
* (This is limited by our use of BitSet32 to track pointer assignments.) */
#define MAX_POINTER_ID 32
/** Amount that trackball needs to move in order to generate a key event. */
#define TRACKBALL_MOVEMENT_THRESHOLD 6
/* Slop distance for jumpy pointer detection.
* The vertical range of the screen divided by this is our epsilon value. */
#define JUMPY_EPSILON_DIVISOR 212
/* Number of jumpy points to drop for touchscreens that need it. */
#define JUMPY_TRANSITION_DROPS 3
#define JUMPY_DROP_LIMIT 3
/* Maximum squared distance for averaging.
* If moving farther than this, turn of averaging to avoid lag in response. */
#define AVERAGING_DISTANCE_LIMIT (75 * 75)
/* Maximum number of historical samples to average. */
#define AVERAGING_HISTORY_SIZE 5
namespace android {
extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
extern int32_t rotateKeyCode(int32_t keyCode, int32_t orientation);
/*
* An input device structure tracks the state of a single input device.
*
* This structure is only used by ReaderThread and is not intended to be shared with
* DispatcherThread (because that would require locking). This works out fine because
* DispatcherThread is only interested in cooked event data anyways and does not need
* any of the low-level data from InputDevice.
*/
struct InputDevice {
struct AbsoluteAxisInfo {
int32_t minValue; // minimum value
int32_t maxValue; // maximum value
int32_t range; // range of values, equal to maxValue - minValue
int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8
int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
};
struct VirtualKey {
int32_t keyCode;
int32_t scanCode;
uint32_t flags;
// computed hit box, specified in touch screen coords based on known display size
int32_t hitLeft;
int32_t hitTop;
int32_t hitRight;
int32_t hitBottom;
inline bool isHit(int32_t x, int32_t y) const {
return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom;
}
};
struct KeyboardState {
struct Current {
int32_t metaState;
nsecs_t downTime; // time of most recent key down
} current;
void reset();
};
struct TrackballState {
struct Accumulator {
enum {
FIELD_BTN_MOUSE = 1,
FIELD_REL_X = 2,
FIELD_REL_Y = 4
};
uint32_t fields;
bool btnMouse;
int32_t relX;
int32_t relY;
inline void clear() {
fields = 0;
}
inline bool isDirty() {
return fields != 0;
}
} accumulator;
struct Current {
bool down;
nsecs_t downTime;
} current;
struct Precalculated {
float xScale;
float yScale;
float xPrecision;
float yPrecision;
} precalculated;
void reset();
};
struct SingleTouchScreenState {
struct Accumulator {
enum {
FIELD_BTN_TOUCH = 1,
FIELD_ABS_X = 2,
FIELD_ABS_Y = 4,
FIELD_ABS_PRESSURE = 8,
FIELD_ABS_TOOL_WIDTH = 16
};
uint32_t fields;
bool btnTouch;
int32_t absX;
int32_t absY;
int32_t absPressure;
int32_t absToolWidth;
inline void clear() {
fields = 0;
}
inline bool isDirty() {
return fields != 0;
}
} accumulator;
struct Current {
bool down;
int32_t x;
int32_t y;
int32_t pressure;
int32_t size;
} current;
void reset();
};
struct MultiTouchScreenState {
struct Accumulator {
enum {
FIELD_ABS_MT_POSITION_X = 1,
FIELD_ABS_MT_POSITION_Y = 2,
FIELD_ABS_MT_TOUCH_MAJOR = 4,
FIELD_ABS_MT_WIDTH_MAJOR = 8,
FIELD_ABS_MT_TRACKING_ID = 16
};
uint32_t pointerCount;
struct Pointer {
uint32_t fields;
int32_t absMTPositionX;
int32_t absMTPositionY;
int32_t absMTTouchMajor;
int32_t absMTWidthMajor;
int32_t absMTTrackingId;
inline void clear() {
fields = 0;
}
} pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
inline void clear() {
pointerCount = 0;
pointers[0].clear();
}
inline bool isDirty() {
return pointerCount != 0;
}
} accumulator;
void reset();
};
struct PointerData {
uint32_t id;
int32_t x;
int32_t y;
int32_t pressure;
int32_t size;
};
struct TouchData {
uint32_t pointerCount;
PointerData pointers[MAX_POINTERS];
BitSet32 idBits;
uint32_t idToIndex[MAX_POINTER_ID];
void copyFrom(const TouchData& other);
inline void clear() {
pointerCount = 0;
idBits.clear();
}
};
// common state used for both single-touch and multi-touch screens after the initial
// touch decoding has been performed
struct TouchScreenState {
Vector<VirtualKey> virtualKeys;
struct Parameters {
bool useBadTouchFilter;
bool useJumpyTouchFilter;
bool useAveragingTouchFilter;
AbsoluteAxisInfo xAxis;
AbsoluteAxisInfo yAxis;
AbsoluteAxisInfo pressureAxis;
AbsoluteAxisInfo sizeAxis;
} parameters;
// The touch data of the current sample being processed.
TouchData currentTouch;
// The touch data of the previous sample that was processed. This is updated
// incrementally while the current sample is being processed.
TouchData lastTouch;
// The time the primary pointer last went down.
nsecs_t downTime;
struct CurrentVirtualKeyState {
bool down;
nsecs_t downTime;
int32_t keyCode;
int32_t scanCode;
} currentVirtualKey;
struct AveragingTouchFilterState {
// Individual history tracks are stored by pointer id
uint32_t historyStart[MAX_POINTERS];
uint32_t historyEnd[MAX_POINTERS];
struct {
struct {
int32_t x;
int32_t y;
int32_t pressure;
} pointers[MAX_POINTERS];
} historyData[AVERAGING_HISTORY_SIZE];
} averagingTouchFilter;
struct JumpTouchFilterState {
int32_t jumpyPointsDropped;
} jumpyTouchFilter;
struct Precalculated {
float xScale;
float yScale;
float pressureScale;
float sizeScale;
} precalculated;
void reset();
bool applyBadTouchFilter();
bool applyJumpyTouchFilter();
void applyAveragingTouchFilter();
void calculatePointerIds();
bool isPointInsideDisplay(int32_t x, int32_t y) const;
};
InputDevice(int32_t id, uint32_t classes, String8 name);
int32_t id;
uint32_t classes;
String8 name;
bool ignored;
KeyboardState keyboard;
TrackballState trackball;
TouchScreenState touchScreen;
union {
SingleTouchScreenState singleTouchScreen;
MultiTouchScreenState multiTouchScreen;
};
void reset();
inline bool isKeyboard() const { return classes & INPUT_DEVICE_CLASS_KEYBOARD; }
inline bool isAlphaKey() const { return classes & INPUT_DEVICE_CLASS_ALPHAKEY; }
inline bool isTrackball() const { return classes & INPUT_DEVICE_CLASS_TRACKBALL; }
inline bool isDPad() const { return classes & INPUT_DEVICE_CLASS_DPAD; }
inline bool isSingleTouchScreen() const { return (classes
& (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT))
== INPUT_DEVICE_CLASS_TOUCHSCREEN; }
inline bool isMultiTouchScreen() const { return classes
& INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; }
inline bool isTouchScreen() const { return classes
& (INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT); }
};
/* Processes raw input events and sends cooked event data to an input dispatcher
* in accordance with the input dispatch policy. */
class InputReaderInterface : public virtual RefBase {
protected:
InputReaderInterface() { }
virtual ~InputReaderInterface() { }
public:
/* Runs a single iteration of the processing loop.
* Nominally reads and processes one incoming message from the EventHub.
*
* This method should be called on the input reader thread.
*/
virtual void loopOnce() = 0;
/* Gets the current virtual key. Returns false if not down.
*
* This method may be called on any thread (usually by the input manager).
*/
virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const = 0;
};
/* The input reader reads raw event data from the event hub and processes it into input events
* that it sends to the input dispatcher. Some functions of the input reader are controlled
* by the input dispatch policy, such as early event filtering in low power states.
*/
class InputReader : public InputReaderInterface {
public:
InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputDispatchPolicyInterface>& policy,
const sp<InputDispatcherInterface>& dispatcher);
virtual ~InputReader();
virtual void loopOnce();
virtual bool getCurrentVirtualKey(int32_t* outKeyCode, int32_t* outScanCode) const;
private:
// Lock that must be acquired while manipulating state that may be concurrently accessed
// from other threads by input state query methods. It should be held for as short a
// time as possible.
//
// Exported state:
// - global virtual key code and scan code
// - device list and immutable properties of devices such as id, name, and class
// (but not other internal device state)
mutable Mutex mExportedStateLock;
// current virtual key information
int32_t mGlobalVirtualKeyCode;
int32_t mGlobalVirtualScanCode;
// combined key meta state
int32_t mGlobalMetaState;
sp<EventHubInterface> mEventHub;
sp<InputDispatchPolicyInterface> mPolicy;
sp<InputDispatcherInterface> mDispatcher;
KeyedVector<int32_t, InputDevice*> mDevices;
// display properties needed to translate touch screen coordinates into display coordinates
int32_t mDisplayOrientation;
int32_t mDisplayWidth;
int32_t mDisplayHeight;
// low-level input event decoding
void process(const RawEvent* rawEvent);
void handleDeviceAdded(const RawEvent* rawEvent);
void handleDeviceRemoved(const RawEvent* rawEvent);
void handleSync(const RawEvent* rawEvent);
void handleKey(const RawEvent* rawEvent);
void handleRelativeMotion(const RawEvent* rawEvent);
void handleAbsoluteMotion(const RawEvent* rawEvent);
void handleSwitch(const RawEvent* rawEvent);
// input policy processing and dispatch
void onKey(nsecs_t when, InputDevice* device, bool down,
int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
void onSwitch(nsecs_t when, InputDevice* device, bool down, int32_t code);
void onSingleTouchScreenStateChanged(nsecs_t when, InputDevice* device);
void onMultiTouchScreenStateChanged(nsecs_t when, InputDevice* device);
void onTouchScreenChanged(nsecs_t when, InputDevice* device, bool havePointerIds);
void onTrackballStateChanged(nsecs_t when, InputDevice* device);
void onConfigurationChanged(nsecs_t when);
bool applyStandardInputDispatchPolicyActions(nsecs_t when,
int32_t policyActions, uint32_t* policyFlags);
bool consumeVirtualKeyTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags);
void dispatchVirtualKey(nsecs_t when, InputDevice* device, uint32_t policyFlags,
int32_t keyEventAction, int32_t keyEventFlags);
void dispatchTouches(nsecs_t when, InputDevice* device, uint32_t policyFlags);
void dispatchTouch(nsecs_t when, InputDevice* device, uint32_t policyFlags,
InputDevice::TouchData* touch, BitSet32 idBits, int32_t motionEventAction);
// display
void resetDisplayProperties();
bool refreshDisplayProperties();
// device management
InputDevice* getDevice(int32_t deviceId);
InputDevice* getNonIgnoredDevice(int32_t deviceId);
void addDevice(nsecs_t when, int32_t deviceId);
void removeDevice(nsecs_t when, InputDevice* device);
void configureDevice(InputDevice* device);
void configureDeviceForCurrentDisplaySize(InputDevice* device);
void configureVirtualKeys(InputDevice* device);
void configureAbsoluteAxisInfo(InputDevice* device, int axis, const char* name,
InputDevice::AbsoluteAxisInfo* out);
// global meta state management for all devices
void resetGlobalMetaState();
int32_t globalMetaState();
// virtual key management
void updateGlobalVirtualKeyState();
};
/* Reads raw events from the event hub and processes them, endlessly. */
class InputReaderThread : public Thread {
public:
InputReaderThread(const sp<InputReaderInterface>& reader);
virtual ~InputReaderThread();
private:
sp<InputReaderInterface> mReader;
virtual bool threadLoop();
};
} // namespace android
#endif // _UI_INPUT_READER_H

331
include/ui/InputTransport.h Normal file
View File

@@ -0,0 +1,331 @@
/*
* 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 _UI_INPUT_TRANSPORT_H
#define _UI_INPUT_TRANSPORT_H
/**
* Native input transport.
*
* Uses anonymous shared memory as a whiteboard for sending input events from an
* InputPublisher to an InputConsumer and ensuring appropriate synchronization.
* One interesting feature is that published events can be updated in place as long as they
* have not yet been consumed.
*
* The InputPublisher and InputConsumer only take care of transferring event data
* over an InputChannel and sending synchronization signals. The InputDispatcher and InputQueue
* build on these abstractions to add multiplexing and queueing.
*/
#include <semaphore.h>
#include <ui/Input.h>
#include <utils/Errors.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
namespace android {
/*
* An input channel consists of a shared memory buffer and a pair of pipes
* used to send input messages from an InputPublisher to an InputConsumer
* across processes. Each channel has a descriptive name for debugging purposes.
*
* Each endpoint has its own InputChannel object that specifies its own file descriptors.
*
* The input channel is closed when all references to it are released.
*/
class InputChannel : public RefBase {
protected:
virtual ~InputChannel();
public:
InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
int32_t sendPipeFd);
/* Creates a pair of input channels and their underlying shared memory buffers
* and pipes.
*
* Returns OK on success.
*/
static status_t openInputChannelPair(const String8& name,
InputChannel** outServerChannel, InputChannel** outClientChannel);
inline String8 getName() const { return mName; }
inline int32_t getAshmemFd() const { return mAshmemFd; }
inline int32_t getReceivePipeFd() const { return mReceivePipeFd; }
inline int32_t getSendPipeFd() const { return mSendPipeFd; }
/* Sends a signal to the other endpoint.
*
* Returns OK on success.
* Errors probably indicate that the channel is broken.
*/
status_t sendSignal(char signal);
/* Receives a signal send by the other endpoint.
* (Should only call this after poll() indicates that the receivePipeFd has available input.)
*
* Returns OK on success.
* Returns WOULD_BLOCK if there is no signal present.
* Other errors probably indicate that the channel is broken.
*/
status_t receiveSignal(char* outSignal);
private:
String8 mName;
int32_t mAshmemFd;
int32_t mReceivePipeFd;
int32_t mSendPipeFd;
};
/*
* Private intermediate representation of input events as messages written into an
* ashmem buffer.
*/
struct InputMessage {
/* Semaphore count is set to 1 when the message is published.
* It becomes 0 transiently while the publisher updates the message.
* It becomes 0 permanently when the consumer consumes the message.
*/
sem_t semaphore;
/* Initialized to false by the publisher.
* Set to true by the consumer when it consumes the message.
*/
bool consumed;
int32_t type;
struct SampleData {
nsecs_t eventTime;
PointerCoords coords[0]; // variable length
};
int32_t deviceId;
int32_t nature;
union {
struct {
int32_t action;
int32_t flags;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
int32_t repeatCount;
nsecs_t downTime;
nsecs_t eventTime;
} key;
struct {
int32_t action;
int32_t metaState;
int32_t edgeFlags;
nsecs_t downTime;
float xOffset;
float yOffset;
float xPrecision;
float yPrecision;
size_t pointerCount;
int32_t pointerIds[MAX_POINTERS];
size_t sampleCount;
SampleData sampleData[0]; // variable length
} motion;
};
/* Gets the number of bytes to add to step to the next SampleData object in a motion
* event message for a given number of pointers.
*/
static inline size_t sampleDataStride(size_t pointerCount) {
return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords);
}
/* Adds the SampleData stride to the given pointer. */
static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) {
return reinterpret_cast<InputMessage::SampleData*>(reinterpret_cast<char*>(ptr) + stride);
}
};
/*
* Publishes input events to an anonymous shared memory buffer.
* Uses atomic operations to coordinate shared access with a single concurrent consumer.
*/
class InputPublisher {
public:
/* Creates a publisher associated with an input channel. */
explicit InputPublisher(const sp<InputChannel>& channel);
/* Destroys the publisher and releases its input channel. */
~InputPublisher();
/* Gets the underlying input channel. */
inline sp<InputChannel> getChannel() { return mChannel; }
/* Prepares the publisher for use. Must be called before it is used.
* Returns OK on success.
*
* This method implicitly calls reset(). */
status_t initialize();
/* Resets the publisher to its initial state and unpins its ashmem buffer.
* Returns OK on success.
*
* Should be called after an event has been consumed to release resources used by the
* publisher until the next event is ready to be published.
*/
status_t reset();
/* Publishes a key event to the ashmem buffer.
*
* Returns OK on success.
* Returns INVALID_OPERATION if the publisher has not been reset.
*/
status_t publishKeyEvent(
int32_t deviceId,
int32_t nature,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime);
/* Publishes a motion event to the ashmem buffer.
*
* Returns OK on success.
* Returns INVALID_OPERATION if the publisher has not been reset.
* Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS.
*/
status_t publishMotionEvent(
int32_t deviceId,
int32_t nature,
int32_t action,
int32_t edgeFlags,
int32_t metaState,
float xOffset,
float yOffset,
float xPrecision,
float yPrecision,
nsecs_t downTime,
nsecs_t eventTime,
size_t pointerCount,
const int32_t* pointerIds,
const PointerCoords* pointerCoords);
/* Appends a motion sample to a motion event unless already consumed.
*
* Returns OK on success.
* Returns INVALID_OPERATION if the current event is not a MOTION_EVENT_ACTION_MOVE event.
* Returns FAILED_TRANSACTION if the current event has already been consumed.
* Returns NO_MEMORY if the buffer is full and no additional samples can be added.
*/
status_t appendMotionSample(
nsecs_t eventTime,
const PointerCoords* pointerCoords);
/* Sends a dispatch signal to the consumer to inform it that a new message is available.
*
* Returns OK on success.
* Errors probably indicate that the channel is broken.
*/
status_t sendDispatchSignal();
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
*
* Returns OK on success.
* Returns WOULD_BLOCK if there is no signal present.
* Other errors probably indicate that the channel is broken.
*/
status_t receiveFinishedSignal();
private:
sp<InputChannel> mChannel;
size_t mAshmemSize;
InputMessage* mSharedMessage;
bool mPinned;
bool mSemaphoreInitialized;
bool mWasDispatched;
size_t mMotionEventPointerCount;
InputMessage::SampleData* mMotionEventSampleDataTail;
size_t mMotionEventSampleDataStride;
status_t publishInputEvent(
int32_t type,
int32_t deviceId,
int32_t nature);
};
/*
* Consumes input events from an anonymous shared memory buffer.
* Uses atomic operations to coordinate shared access with a single concurrent publisher.
*/
class InputConsumer {
public:
/* Creates a consumer associated with an input channel. */
explicit InputConsumer(const sp<InputChannel>& channel);
/* Destroys the consumer and releases its input channel. */
~InputConsumer();
/* Gets the underlying input channel. */
inline sp<InputChannel> getChannel() { return mChannel; }
/* Prepares the consumer for use. Must be called before it is used. */
status_t initialize();
/* Consumes the input event in the buffer and copies its contents into
* an InputEvent object created using the specified factory.
* This operation will block if the publisher is updating the event.
*
* Returns OK on success.
* Returns INVALID_OPERATION if there is no currently published event.
* Returns NO_MEMORY if the event could not be created.
*/
status_t consume(InputEventFactoryInterface* factory, InputEvent** event);
/* Sends a finished signal to the publisher to inform it that the current message is
* finished processing.
*
* Returns OK on success.
* Errors probably indicate that the channel is broken.
*/
status_t sendFinishedSignal();
/* Receives the dispatched signal from the publisher.
*
* Returns OK on success.
* Returns WOULD_BLOCK if there is no signal present.
* Other errors probably indicate that the channel is broken.
*/
status_t receiveDispatchSignal();
private:
sp<InputChannel> mChannel;
size_t mAshmemSize;
InputMessage* mSharedMessage;
void populateKeyEvent(KeyEvent* keyEvent) const;
void populateMotionEvent(MotionEvent* motionEvent) const;
};
} // namespace android
#endif // _UI_INPUT_TRANSPORT_H

67
include/utils/BitSet.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* 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 UTILS_BITSET_H
#define UTILS_BITSET_H
#include <stdint.h>
/*
* Contains some bit manipulation helpers.
*/
namespace android {
// A simple set of 32 bits that can be individually marked or cleared.
struct BitSet32 {
uint32_t value;
inline BitSet32() : value(0) { }
explicit inline BitSet32(uint32_t value) : value(value) { }
// Gets the value associated with a particular bit index.
static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; }
// Clears the bit set.
inline void clear() { value = 0; }
// Returns true if the bit set does not contain any marked bits.
inline bool isEmpty() const { return ! value; }
// Returns true if the specified bit is marked.
inline bool hasBit(uint32_t n) const { return value & valueForBit(n); }
// Marks the specified bit.
inline void markBit(uint32_t n) { value |= valueForBit(n); }
// Clears the specified bit.
inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); }
// Finds the first marked bit in the set.
// Result is undefined if all bits are unmarked.
inline uint32_t firstMarkedBit() const { return __builtin_clz(value); }
// Finds the first unmarked bit in the set.
// Result is undefined if all bits are marked.
inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); }
inline bool operator== (const BitSet32& other) const { return value == other.value; }
inline bool operator!= (const BitSet32& other) const { return value != other.value; }
};
} // namespace android
#endif // UTILS_BITSET_H

View File

@@ -1,107 +0,0 @@
/*
* Copyright (C) 2008 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 __UTILS_BUFFER_H__
#define __UTILS_BUFFER_H__ 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
namespace android {
class Buffer
{
private:
char *buf;
int bufsiz;
int used;
void ensureCapacity(int len);
void
makeRoomFor(int len)
{
if (len + used >= bufsiz) {
bufsiz = (len + used) * 3/2 + 2;
char *blah = new char[bufsiz];
memcpy(blah, buf, used);
delete[] buf;
buf = blah;
}
}
public:
Buffer()
{
bufsiz = 16;
buf = new char[bufsiz];
clear();
}
~Buffer()
{
delete[] buf;
}
void
clear()
{
buf[0] = '\0';
used = 0;
}
int
length()
{
return used;
}
void
append(const char c)
{
makeRoomFor(1);
buf[used] = c;
used++;
buf[used] = '\0';
}
void
append(const char *s, int len)
{
makeRoomFor(len);
memcpy(buf + used, s, len);
used += len;
buf[used] = '\0';
}
void
append(const char *s)
{
append(s, strlen(s));
}
char *
getBytes()
{
return buf;
}
};
}; // namespace android
#endif

137
include/utils/PollLoop.h Normal file
View File

@@ -0,0 +1,137 @@
/*
* 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 UTILS_POLL_LOOP_H
#define UTILS_POLL_LOOP_H
#include <utils/Vector.h>
#include <utils/threads.h>
#include <poll.h>
namespace android {
/**
* A basic file descriptor polling loop based on poll() with callbacks.
*/
class PollLoop : public RefBase {
protected:
virtual ~PollLoop();
public:
PollLoop();
/**
* A callback that it to be invoked when an event occurs on a file descriptor.
* Specifies the events that were triggered and the user data provided when the
* callback was set.
*
* Returns true if the callback should be kept, false if it should be removed automatically
* after the callback returns.
*/
typedef bool (*Callback)(int fd, int events, void* data);
/**
* Performs a single call to poll() with optional timeout in milliseconds.
* Invokes callbacks for all file descriptors on which an event occurred.
*
* If the timeout is zero, returns immediately without blocking.
* If the timeout is negative, waits indefinitely until awoken.
*
* Returns true if a callback was invoked or if the loop was awoken by wake().
* Returns false if a timeout or error occurred.
*
* This method must only be called on the main thread.
* This method blocks until either a file descriptor is signalled, a timeout occurs,
* or wake() is called.
* This method does not return until it has finished invoking the appropriate callbacks
* for all file descriptors that were signalled.
*/
bool pollOnce(int timeoutMillis);
/**
* Wakes the loop asynchronously.
*
* This method can be called on any thread.
* This method returns immediately.
*/
void wake();
/**
* Sets the callback for a file descriptor, replacing the existing one, if any.
* It is an error to call this method with events == 0 or callback == NULL.
*
* Note that a callback can be invoked with the POLLERR, POLLHUP or POLLNVAL events
* even if it is not explicitly requested when registered.
*
* This method can be called on any thread.
* This method may block briefly if it needs to wake the poll loop.
*/
void setCallback(int fd, int events, Callback callback, void* data = NULL);
/**
* Removes the callback for a file descriptor, if one exists.
*
* When this method returns, it is safe to close the file descriptor since the poll loop
* will no longer have a reference to it. However, it is possible for the callback to
* already be running or for it to run one last time if the file descriptor was already
* signalled. Calling code is responsible for ensuring that this case is safely handled.
* For example, if the callback takes care of removing itself during its own execution either
* by returning false or calling this method, then it can be guaranteed to not be invoked
* again at any later time unless registered anew.
*
* This method can be called on any thread.
* This method may block briefly if it needs to wake the poll loop.
*
* Returns true if a callback was actually removed, false if none was registered.
*/
bool removeCallback(int fd);
private:
struct RequestedCallback {
Callback callback;
void* data;
};
struct PendingCallback {
int fd;
int events;
Callback callback;
void* data;
};
Mutex mLock;
Condition mAwake;
bool mPolling;
int mWakeReadPipeFd;
int mWakeWritePipeFd;
Vector<struct pollfd> mRequestedFds;
Vector<RequestedCallback> mRequestedCallbacks;
Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce
void openWakePipe();
void closeWakePipe();
ssize_t getRequestIndexLocked(int fd);
void wakeAndLock();
};
} // namespace android
#endif // UTILS_POLL_LOOP_H

71
include/utils/Pool.h Normal file
View File

@@ -0,0 +1,71 @@
/*
* 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 UTILS_POOL_H
#define UTILS_POOL_H
#include <utils/TypeHelpers.h>
namespace android {
class PoolImpl {
public:
PoolImpl(size_t objSize);
~PoolImpl();
void* allocImpl();
void freeImpl(void* obj);
private:
size_t mObjSize;
};
/*
* A homogeneous typed memory pool for fixed size objects.
* Not intended to be thread-safe.
*/
template<typename T>
class Pool : private PoolImpl {
public:
/* Creates an initially empty pool. */
Pool() : PoolImpl(sizeof(T)) { }
/* Destroys the pool.
* Assumes that the pool is empty. */
~Pool() { }
/* Allocates an object from the pool, growing the pool if needed. */
inline T* alloc() {
void* mem = allocImpl();
if (! traits<T>::has_trivial_ctor) {
return new (mem) T();
} else {
return static_cast<T*>(mem);
}
}
/* Frees an object from the pool. */
inline void free(T* obj) {
if (! traits<T>::has_trivial_dtor) {
obj->~T();
}
freeImpl(obj);
}
};
} // namespace android
#endif // UTILS_POOL_H

View File

@@ -37,6 +37,8 @@ public:
const char* name() const;
nsecs_t lap();
nsecs_t elapsedTime() const;
void reset();
private:
const char* mName;

View File

@@ -114,6 +114,12 @@ public:
ssize_t appendVector(const Vector<TYPE>& vector);
//! insert an array at a given index
ssize_t insertArrayAt(const TYPE* array, size_t index, size_t numItems);
//! append an array at the end of this vector
ssize_t appendArray(const TYPE* array, size_t numItems);
/*!
* add/insert/replace items
*/
@@ -258,6 +264,16 @@ ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
}
template<class TYPE> inline
ssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t numItems) {
return VectorImpl::insertAt(array, index, numItems);
}
template<class TYPE> inline
ssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t numItems) {
return VectorImpl::add(array, numItems);
}
template<class TYPE> inline
ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
return VectorImpl::insertAt(&item, index, numItems);

View File

@@ -76,7 +76,7 @@ public:
void push();
void push(const void* item);
ssize_t add();
ssize_t add(const void* item);
ssize_t add(const void* item, size_t numItems = 1);
ssize_t replaceAt(size_t index);
ssize_t replaceAt(const void* item, size_t index);
@@ -184,6 +184,8 @@ private:
void push(const void* item);
ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
ssize_t appendVector(const VectorImpl& vector);
ssize_t insertArrayAt(const void* array, size_t index, size_t numItems);
ssize_t appendArray(const void* array, size_t numItems);
ssize_t insertAt(size_t where, size_t numItems = 1);
ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
ssize_t replaceAt(size_t index);

View File

@@ -11,6 +11,11 @@ LOCAL_SRC_FILES:= \
GraphicBufferMapper.cpp \
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
Input.cpp \
InputDispatcher.cpp \
InputManager.cpp \
InputReader.cpp \
InputTransport.cpp \
IOverlay.cpp \
Overlay.cpp \
PixelFormat.cpp \

View File

@@ -155,77 +155,70 @@ int EventHub::getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
return 0;
}
int EventHub::getSwitchState(int sw) const
{
#ifdef EV_SW
if (sw >= 0 && sw <= SW_MAX) {
int32_t devid = mSwitches[sw];
if (devid != 0) {
return getSwitchState(devid, sw);
int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t scanCode) const {
if (scanCode >= 0 && scanCode <= KEY_MAX) {
AutoMutex _l(mLock);
if (deviceId == -1) {
for (int i = 0; i < mNumDevicesById; i++) {
device_t* device = mDevicesById[i].device;
if (device != NULL && (device->classes & deviceClasses) != 0) {
int32_t result = getScanCodeStateLocked(device, scanCode);
if (result >= KEY_STATE_DOWN) {
return result;
}
}
}
return KEY_STATE_UP;
} else {
device_t* device = getDevice(deviceId);
if (device != NULL) {
return getScanCodeStateLocked(device, scanCode);
}
}
}
#endif
return -1;
return KEY_STATE_UNKNOWN;
}
int EventHub::getSwitchState(int32_t deviceId, int sw) const
{
#ifdef EV_SW
AutoMutex _l(mLock);
device_t* device = getDevice(deviceId);
if (device == NULL) return -1;
if (sw >= 0 && sw <= SW_MAX) {
uint8_t sw_bitmask[(SW_MAX+7)/8];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
if (ioctl(mFDs[id_to_index(device->id)].fd,
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
return test_bit(sw, sw_bitmask) ? 1 : 0;
int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
uint8_t key_bitmask[(KEY_MAX + 7) / 8];
memset(key_bitmask, 0, sizeof(key_bitmask));
if (ioctl(mFDs[id_to_index(device->id)].fd,
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
return test_bit(scanCode, key_bitmask) ? KEY_STATE_DOWN : KEY_STATE_UP;
}
return KEY_STATE_UNKNOWN;
}
int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t keyCode) const {
if (deviceId == -1) {
for (int i = 0; i < mNumDevicesById; i++) {
device_t* device = mDevicesById[i].device;
if (device != NULL && (device->classes & deviceClasses) != 0) {
int32_t result = getKeyCodeStateLocked(device, keyCode);
if (result >= KEY_STATE_DOWN) {
return result;
}
}
}
return KEY_STATE_UP;
} else {
device_t* device = getDevice(deviceId);
if (device != NULL) {
return getKeyCodeStateLocked(device, keyCode);
}
}
#endif
return -1;
return KEY_STATE_UNKNOWN;
}
int EventHub::getScancodeState(int code) const
{
return getScancodeState(mFirstKeyboardId, code);
}
int EventHub::getScancodeState(int32_t deviceId, int code) const
{
AutoMutex _l(mLock);
device_t* device = getDevice(deviceId);
if (device == NULL) return -1;
if (code >= 0 && code <= KEY_MAX) {
uint8_t key_bitmask[(KEY_MAX+7)/8];
memset(key_bitmask, 0, sizeof(key_bitmask));
if (ioctl(mFDs[id_to_index(device->id)].fd,
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
return test_bit(code, key_bitmask) ? 1 : 0;
}
}
return -1;
}
int EventHub::getKeycodeState(int code) const
{
return getKeycodeState(mFirstKeyboardId, code);
}
int EventHub::getKeycodeState(int32_t deviceId, int code) const
{
AutoMutex _l(mLock);
device_t* device = getDevice(deviceId);
if (device == NULL || device->layoutMap == NULL) return -1;
int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const {
Vector<int32_t> scanCodes;
device->layoutMap->findScancodes(code, &scanCodes);
uint8_t key_bitmask[(KEY_MAX+7)/8];
device->layoutMap->findScancodes(keyCode, &scanCodes);
uint8_t key_bitmask[(KEY_MAX + 7) / 8];
memset(key_bitmask, 0, sizeof(key_bitmask));
if (ioctl(mFDs[id_to_index(device->id)].fd,
EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
@@ -239,12 +232,45 @@ int EventHub::getKeycodeState(int32_t deviceId, int code) const
int32_t sc = scanCodes.itemAt(i);
//LOGI("Code %d: down=%d", sc, test_bit(sc, key_bitmask));
if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, key_bitmask)) {
return 1;
return KEY_STATE_DOWN;
}
}
return KEY_STATE_UP;
}
return 0;
return KEY_STATE_UNKNOWN;
}
int32_t EventHub::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const {
#ifdef EV_SW
if (sw >= 0 && sw <= SW_MAX) {
AutoMutex _l(mLock);
if (deviceId == -1) {
deviceId = mSwitches[sw];
if (deviceId == 0) {
return KEY_STATE_UNKNOWN;
}
}
device_t* device = getDevice(deviceId);
if (device == NULL) {
return KEY_STATE_UNKNOWN;
}
return getSwitchStateLocked(device, sw);
}
#endif
return KEY_STATE_UNKNOWN;
}
int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
uint8_t sw_bitmask[(SW_MAX + 7) / 8];
memset(sw_bitmask, 0, sizeof(sw_bitmask));
if (ioctl(mFDs[id_to_index(device->id)].fd,
EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
return test_bit(sw, sw_bitmask) ? KEY_STATE_DOWN : KEY_STATE_UP;
}
return KEY_STATE_UNKNOWN;
}
status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
@@ -309,9 +335,6 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
status_t err;
fd_set readfds;
int maxFd = -1;
int cc;
int i;
int res;
int pollres;
@@ -457,7 +480,7 @@ bool EventHub::openPlatformInput(void)
* Inspect the known devices to determine whether physical keys exist for the given
* framework-domain key codes.
*/
bool EventHub::hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags) {
bool EventHub::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) {
outFlags[codeIndex] = 0;
@@ -465,7 +488,8 @@ bool EventHub::hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags) {
Vector<int32_t> scanCodes;
for (int n = 0; (n < mFDCount) && (outFlags[codeIndex] == 0); n++) {
if (mDevices[n]) {
status_t err = mDevices[n]->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes);
status_t err = mDevices[n]->layoutMap->findScancodes(
keyCodes[codeIndex], &scanCodes);
if (!err) {
// check the possible scan codes identified by the layout map against the
// map of codes actually emitted by the driver
@@ -618,11 +642,11 @@ int EventHub::open_device(const char *deviceName)
//}
for (int i=0; i<((BTN_MISC+7)/8); i++) {
if (key_bitmask[i] != 0) {
device->classes |= CLASS_KEYBOARD;
device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;
break;
}
}
if ((device->classes & CLASS_KEYBOARD) != 0) {
if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
if (device->keyBitmask != NULL) {
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
@@ -642,7 +666,7 @@ int EventHub::open_device(const char *deviceName)
if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0)
{
if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
device->classes |= CLASS_TRACKBALL;
device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;
}
}
}
@@ -656,12 +680,12 @@ int EventHub::open_device(const char *deviceName)
if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
&& test_bit(ABS_MT_POSITION_X, abs_bitmask)
&& test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT;
device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT;
// Is this an old style single-touch driver?
} else if (test_bit(BTN_TOUCH, key_bitmask)
&& test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
device->classes |= CLASS_TOUCHSCREEN;
device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN;
}
#ifdef EV_SW
@@ -680,7 +704,7 @@ int EventHub::open_device(const char *deviceName)
}
#endif
if ((device->classes&CLASS_KEYBOARD) != 0) {
if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) {
char tmpfn[sizeof(name)];
char keylayoutFilename[300];
@@ -723,7 +747,7 @@ int EventHub::open_device(const char *deviceName)
// 'Q' key support = cheap test of whether this is an alpha-capable kbd
if (hasKeycode(device, kKeyCodeQ)) {
device->classes |= CLASS_ALPHAKEY;
device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
}
// See if this has a DPAD.
@@ -732,7 +756,7 @@ int EventHub::open_device(const char *deviceName)
hasKeycode(device, kKeyCodeDpadLeft) &&
hasKeycode(device, kKeyCodeDpadRight) &&
hasKeycode(device, kKeyCodeDpadCenter)) {
device->classes |= CLASS_DPAD;
device->classes |= INPUT_DEVICE_CLASS_DPAD;
}
LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",

238
libs/ui/Input.cpp Normal file
View File

@@ -0,0 +1,238 @@
//
// Copyright 2010 The Android Open Source Project
//
// Provides a pipe-based transport for native events in the NDK.
//
#define LOG_TAG "Input"
//#define LOG_NDEBUG 0
#include <ui/Input.h>
namespace android {
// class InputEvent
void InputEvent::initialize(int32_t deviceId, int32_t nature) {
mDeviceId = deviceId;
mNature = nature;
}
// class KeyEvent
void KeyEvent::initialize(
int32_t deviceId,
int32_t nature,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
InputEvent::initialize(deviceId, nature);
mAction = action;
mFlags = flags;
mKeyCode = keyCode;
mScanCode = scanCode;
mMetaState = metaState;
mRepeatCount = repeatCount;
mDownTime = downTime;
mEventTime = eventTime;
}
// class MotionEvent
void MotionEvent::initialize(
int32_t deviceId,
int32_t nature,
int32_t action,
int32_t edgeFlags,
int32_t metaState,
float rawX,
float rawY,
float xPrecision,
float yPrecision,
nsecs_t downTime,
nsecs_t eventTime,
size_t pointerCount,
const int32_t* pointerIds,
const PointerCoords* pointerCoords) {
InputEvent::initialize(deviceId, nature);
mAction = action;
mEdgeFlags = edgeFlags;
mMetaState = metaState;
mRawX = rawX;
mRawY = rawY;
mXPrecision = xPrecision;
mYPrecision = yPrecision;
mDownTime = downTime;
mPointerIds.clear();
mPointerIds.appendArray(pointerIds, pointerCount);
mSampleEventTimes.clear();
mSamplePointerCoords.clear();
addSample(eventTime, pointerCoords);
}
void MotionEvent::addSample(
int64_t eventTime,
const PointerCoords* pointerCoords) {
mSampleEventTimes.push(eventTime);
mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
}
void MotionEvent::offsetLocation(float xOffset, float yOffset) {
if (xOffset != 0 || yOffset != 0) {
for (size_t i = 0; i < mSamplePointerCoords.size(); i++) {
PointerCoords& pointerCoords = mSamplePointerCoords.editItemAt(i);
pointerCoords.x += xOffset;
pointerCoords.y += yOffset;
}
}
}
} // namespace android
// NDK APIs
using android::InputEvent;
using android::KeyEvent;
using android::MotionEvent;
int32_t input_event_get_type(const input_event_t* event) {
return reinterpret_cast<const InputEvent*>(event)->getType();
}
int32_t input_event_get_device_id(const input_event_t* event) {
return reinterpret_cast<const InputEvent*>(event)->getDeviceId();
}
int32_t input_event_get_nature(const input_event_t* event) {
return reinterpret_cast<const InputEvent*>(event)->getNature();
}
int32_t key_event_get_action(const input_event_t* key_event) {
return reinterpret_cast<const KeyEvent*>(key_event)->getAction();
}
int32_t key_event_get_flags(const input_event_t* key_event) {
return reinterpret_cast<const KeyEvent*>(key_event)->getFlags();
}
int32_t key_event_get_key_code(const input_event_t* key_event) {
return reinterpret_cast<const KeyEvent*>(key_event)->getKeyCode();
}
int32_t key_event_get_scan_code(const input_event_t* key_event) {
return reinterpret_cast<const KeyEvent*>(key_event)->getScanCode();
}
int32_t key_event_get_meta_state(const input_event_t* key_event) {
return reinterpret_cast<const KeyEvent*>(key_event)->getMetaState();
}
int32_t key_event_get_repeat_count(const input_event_t* key_event) {
return reinterpret_cast<const KeyEvent*>(key_event)->getRepeatCount();
}
int64_t key_event_get_down_time(const input_event_t* key_event) {
return reinterpret_cast<const KeyEvent*>(key_event)->getDownTime();
}
int64_t key_event_get_event_time(const input_event_t* key_event) {
return reinterpret_cast<const KeyEvent*>(key_event)->getEventTime();
}
int32_t motion_event_get_action(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getAction();
}
int32_t motion_event_get_meta_state(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getMetaState();
}
int32_t motion_event_get_edge_flags(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getEdgeFlags();
}
int64_t motion_event_get_down_time(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getDownTime();
}
int64_t motion_event_get_event_time(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getEventTime();
}
float motion_event_get_x_precision(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getXPrecision();
}
float motion_event_get_y_precision(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getYPrecision();
}
size_t motion_event_get_pointer_count(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getPointerCount();
}
int32_t motion_event_get_pointer_id(const input_event_t* motion_event, size_t pointer_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getPointerId(pointer_index);
}
float motion_event_get_raw_x(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getRawX();
}
float motion_event_get_raw_y(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getRawY();
}
float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getX(pointer_index);
}
float motion_event_get_y(const input_event_t* motion_event, size_t pointer_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getY(pointer_index);
}
float motion_event_get_pressure(const input_event_t* motion_event, size_t pointer_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getPressure(pointer_index);
}
float motion_event_get_size(const input_event_t* motion_event, size_t pointer_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getSize(pointer_index);
}
size_t motion_event_get_history_size(const input_event_t* motion_event) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getHistorySize();
}
int64_t motion_event_get_historical_event_time(input_event_t* motion_event,
size_t history_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalEventTime(
history_index);
}
float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index,
size_t history_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalX(
pointer_index, history_index);
}
float motion_event_get_historical_y(input_event_t* motion_event, size_t pointer_index,
size_t history_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalY(
pointer_index, history_index);
}
float motion_event_get_historical_pressure(input_event_t* motion_event, size_t pointer_index,
size_t history_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalPressure(
pointer_index, history_index);
}
float motion_event_get_historical_size(input_event_t* motion_event, size_t pointer_index,
size_t history_index) {
return reinterpret_cast<const MotionEvent*>(motion_event)->getHistoricalSize(
pointer_index, history_index);
}

1315
libs/ui/InputDispatcher.cpp Normal file

File diff suppressed because it is too large Load Diff

114
libs/ui/InputManager.cpp Normal file
View File

@@ -0,0 +1,114 @@
//
// Copyright 2010 The Android Open Source Project
//
// The input manager.
//
#define LOG_TAG "InputManager"
//#define LOG_NDEBUG 0
#include <cutils/log.h>
#include <ui/InputManager.h>
#include <ui/InputReader.h>
#include <ui/InputDispatcher.h>
namespace android {
InputManager::InputManager(const sp<EventHubInterface>& eventHub,
const sp<InputDispatchPolicyInterface>& policy) :
mEventHub(eventHub), mPolicy(policy) {
mDispatcher = new InputDispatcher(policy);
mReader = new InputReader(eventHub, policy, mDispatcher);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
mReaderThread = new InputReaderThread(mReader);
configureExcludedDevices();
}
InputManager::~InputManager() {
stop();
}
void InputManager::configureExcludedDevices() {
Vector<String8> excludedDeviceNames;
mPolicy->getExcludedDeviceNames(excludedDeviceNames);
for (size_t i = 0; i < excludedDeviceNames.size(); i++) {
mEventHub->addExcludedDevice(excludedDeviceNames[i]);
}
}
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
if (result) {
LOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
}
result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
if (result) {
LOGE("Could not start InputReader thread due to error %d.", result);
mDispatcherThread->requestExit();
return result;
}
return OK;
}
status_t InputManager::stop() {
status_t result = mReaderThread->requestExitAndWait();
if (result) {
LOGW("Could not stop InputReader thread due to error %d.", result);
}
result = mDispatcherThread->requestExitAndWait();
if (result) {
LOGW("Could not stop InputDispatcher thread due to error %d.", result);
}
return OK;
}
status_t InputManager::registerInputChannel(const sp<InputChannel>& inputChannel) {
return mDispatcher->registerInputChannel(inputChannel);
}
status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
return mDispatcher->unregisterInputChannel(inputChannel);
}
int32_t InputManager::getScanCodeState(int32_t deviceId, int32_t deviceClasses, int32_t scanCode)
const {
int32_t vkKeyCode, vkScanCode;
if (mReader->getCurrentVirtualKey(& vkKeyCode, & vkScanCode)) {
if (vkScanCode == scanCode) {
return KEY_STATE_VIRTUAL;
}
}
return mEventHub->getScanCodeState(deviceId, deviceClasses, scanCode);
}
int32_t InputManager::getKeyCodeState(int32_t deviceId, int32_t deviceClasses, int32_t keyCode)
const {
int32_t vkKeyCode, vkScanCode;
if (mReader->getCurrentVirtualKey(& vkKeyCode, & vkScanCode)) {
if (vkKeyCode == keyCode) {
return KEY_STATE_VIRTUAL;
}
}
return mEventHub->getKeyCodeState(deviceId, deviceClasses, keyCode);
}
int32_t InputManager::getSwitchState(int32_t deviceId, int32_t deviceClasses, int32_t sw) const {
return mEventHub->getSwitchState(deviceId, deviceClasses, sw);
}
bool InputManager::hasKeys(size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const {
return mEventHub->hasKeys(numCodes, keyCodes, outFlags);
}
} // namespace android

1844
libs/ui/InputReader.cpp Normal file

File diff suppressed because it is too large Load Diff

684
libs/ui/InputTransport.cpp Normal file
View File

@@ -0,0 +1,684 @@
//
// Copyright 2010 The Android Open Source Project
//
// Provides a shared memory transport for input events.
//
#define LOG_TAG "InputTransport"
//#define LOG_NDEBUG 0
// Log debug messages about channel signalling (send signal, receive signal)
#define DEBUG_CHANNEL_SIGNALS 1
// Log debug messages whenever InputChannel objects are created/destroyed
#define DEBUG_CHANNEL_LIFECYCLE 1
// Log debug messages about transport actions (initialize, reset, publish, ...)
#define DEBUG_TRANSPORT_ACTIONS 1
#include <cutils/ashmem.h>
#include <cutils/log.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <ui/InputTransport.h>
#include <unistd.h>
namespace android {
// Must be at least sizeof(InputMessage) + sufficient space for pointer data
static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384;
// Signal sent by the producer to the consumer to inform it that a new message is
// available to be consumed in the shared memory buffer.
static const char INPUT_SIGNAL_DISPATCH = 'D';
// Signal sent by the consumer to the producer to inform it that it has finished
// consuming the most recent message.
static const char INPUT_SIGNAL_FINISHED = 'f';
// --- InputChannel ---
InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd,
int32_t sendPipeFd) :
mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) {
#if DEBUG_CHANNEL_LIFECYCLE
LOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
mName.string(), ashmemFd, receivePipeFd, sendPipeFd);
#endif
int result = fcntl(mReceivePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make receive pipe "
"non-blocking. errno=%d", mName.string(), errno);
result = fcntl(mSendPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make send pipe "
"non-blocking. errno=%d", mName.string(), errno);
}
InputChannel::~InputChannel() {
#if DEBUG_CHANNEL_LIFECYCLE
LOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d",
mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd);
#endif
::close(mAshmemFd);
::close(mReceivePipeFd);
::close(mSendPipeFd);
}
status_t InputChannel::openInputChannelPair(const String8& name,
InputChannel** outServerChannel, InputChannel** outClientChannel) {
status_t result;
int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE);
if (serverAshmemFd < 0) {
result = -errno;
LOGE("channel '%s' ~ Could not create shared memory region. errno=%d",
name.string(), errno);
} else {
result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE);
if (result < 0) {
LOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.",
name.string(), result, serverAshmemFd);
} else {
// Dup the file descriptor because the server and client input channel objects that
// are returned may have different lifetimes but they share the same shared memory region.
int clientAshmemFd;
clientAshmemFd = dup(serverAshmemFd);
if (clientAshmemFd < 0) {
result = -errno;
LOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d",
name.string(), errno);
} else {
int forward[2];
if (pipe(forward)) {
result = -errno;
LOGE("channel '%s' ~ Could not create forward pipe. errno=%d",
name.string(), errno);
} else {
int reverse[2];
if (pipe(reverse)) {
result = -errno;
LOGE("channel '%s' ~ Could not create reverse pipe. errno=%d",
name.string(), errno);
} else {
String8 serverChannelName = name;
serverChannelName.append(" (server)");
*outServerChannel = new InputChannel(serverChannelName,
serverAshmemFd, reverse[0], forward[1]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
*outClientChannel = new InputChannel(clientChannelName,
clientAshmemFd, forward[0], reverse[1]);
return OK;
}
::close(forward[0]);
::close(forward[1]);
}
::close(clientAshmemFd);
}
}
::close(serverAshmemFd);
}
*outServerChannel = NULL;
*outClientChannel = NULL;
return result;
}
status_t InputChannel::sendSignal(char signal) {
ssize_t nWrite = ::write(mSendPipeFd, & signal, 1);
if (nWrite == 1) {
#if DEBUG_CHANNEL_SIGNALS
LOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal);
#endif
return OK;
}
#if DEBUG_CHANNEL_SIGNALS
LOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno);
#endif
return -errno;
}
status_t InputChannel::receiveSignal(char* outSignal) {
ssize_t nRead = ::read(mReceivePipeFd, outSignal, 1);
if (nRead == 1) {
#if DEBUG_CHANNEL_SIGNALS
LOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal);
#endif
return OK;
}
if (errno == EAGAIN) {
#if DEBUG_CHANNEL_SIGNALS
LOGD("channel '%s' ~ receive signal failed because no signal available", mName.string());
#endif
return WOULD_BLOCK;
}
#if DEBUG_CHANNEL_SIGNALS
LOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno);
#endif
return -errno;
}
// --- InputPublisher ---
InputPublisher::InputPublisher(const sp<InputChannel>& channel) :
mChannel(channel), mSharedMessage(NULL),
mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false),
mMotionEventSampleDataTail(NULL) {
}
InputPublisher::~InputPublisher() {
reset();
if (mSharedMessage) {
munmap(mSharedMessage, mAshmemSize);
}
}
status_t InputPublisher::initialize() {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ initialize",
mChannel->getName().string());
#endif
int ashmemFd = mChannel->getAshmemFd();
int result = ashmem_get_size_region(ashmemFd);
if (result < 0) {
LOGE("channel '%s' publisher ~ Error %d getting size of ashmem fd %d.",
mChannel->getName().string(), result, ashmemFd);
return UNKNOWN_ERROR;
}
mAshmemSize = (size_t) result;
mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
if (! mSharedMessage) {
LOGE("channel '%s' publisher ~ mmap failed on ashmem fd %d.",
mChannel->getName().string(), ashmemFd);
return NO_MEMORY;
}
mPinned = true;
mSharedMessage->consumed = false;
return reset();
}
status_t InputPublisher::reset() {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ reset",
mChannel->getName().string());
#endif
if (mPinned) {
// Destroy the semaphore since we are about to unpin the memory region that contains it.
int result;
if (mSemaphoreInitialized) {
if (mSharedMessage->consumed) {
result = sem_post(& mSharedMessage->semaphore);
if (result < 0) {
LOGE("channel '%s' publisher ~ Error %d in sem_post.",
mChannel->getName().string(), errno);
return UNKNOWN_ERROR;
}
}
result = sem_destroy(& mSharedMessage->semaphore);
if (result < 0) {
LOGE("channel '%s' publisher ~ Error %d in sem_destroy.",
mChannel->getName().string(), errno);
return UNKNOWN_ERROR;
}
mSemaphoreInitialized = false;
}
// Unpin the region since we no longer care about its contents.
int ashmemFd = mChannel->getAshmemFd();
result = ashmem_unpin_region(ashmemFd, 0, 0);
if (result < 0) {
LOGE("channel '%s' publisher ~ Error %d unpinning ashmem fd %d.",
mChannel->getName().string(), result, ashmemFd);
return UNKNOWN_ERROR;
}
mPinned = false;
}
mMotionEventSampleDataTail = NULL;
mWasDispatched = false;
return OK;
}
status_t InputPublisher::publishInputEvent(
int32_t type,
int32_t deviceId,
int32_t nature) {
if (mPinned) {
LOGE("channel '%s' publisher ~ Attempted to publish a new event but publisher has "
"not yet been reset.", mChannel->getName().string());
return INVALID_OPERATION;
}
// Pin the region.
// We do not check for ASHMEM_NOT_PURGED because we don't care about the previous
// contents of the buffer so it does not matter whether it was purged in the meantime.
int ashmemFd = mChannel->getAshmemFd();
int result = ashmem_pin_region(ashmemFd, 0, 0);
if (result < 0) {
LOGE("channel '%s' publisher ~ Error %d pinning ashmem fd %d.",
mChannel->getName().string(), result, ashmemFd);
return UNKNOWN_ERROR;
}
mPinned = true;
result = sem_init(& mSharedMessage->semaphore, 1, 1);
if (result < 0) {
LOGE("channel '%s' publisher ~ Error %d in sem_init.",
mChannel->getName().string(), errno);
return UNKNOWN_ERROR;
}
mSemaphoreInitialized = true;
mSharedMessage->consumed = false;
mSharedMessage->type = type;
mSharedMessage->deviceId = deviceId;
mSharedMessage->nature = nature;
return OK;
}
status_t InputPublisher::publishKeyEvent(
int32_t deviceId,
int32_t nature,
int32_t action,
int32_t flags,
int32_t keyCode,
int32_t scanCode,
int32_t metaState,
int32_t repeatCount,
nsecs_t downTime,
nsecs_t eventTime) {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, nature=%d, "
"action=%d, flags=%d, keyCode=%d, scanCode=%d, metaState=%d, repeatCount=%d,"
"downTime=%lld, eventTime=%lld",
mChannel->getName().string(),
deviceId, nature, action, flags, keyCode, scanCode, metaState, repeatCount,
downTime, eventTime);
#endif
status_t result = publishInputEvent(INPUT_EVENT_TYPE_KEY, deviceId, nature);
if (result < 0) {
return result;
}
mSharedMessage->key.action = action;
mSharedMessage->key.flags = flags;
mSharedMessage->key.keyCode = keyCode;
mSharedMessage->key.scanCode = scanCode;
mSharedMessage->key.metaState = metaState;
mSharedMessage->key.repeatCount = repeatCount;
mSharedMessage->key.downTime = downTime;
mSharedMessage->key.eventTime = eventTime;
return OK;
}
status_t InputPublisher::publishMotionEvent(
int32_t deviceId,
int32_t nature,
int32_t action,
int32_t edgeFlags,
int32_t metaState,
float xOffset,
float yOffset,
float xPrecision,
float yPrecision,
nsecs_t downTime,
nsecs_t eventTime,
size_t pointerCount,
const int32_t* pointerIds,
const PointerCoords* pointerCoords) {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, nature=%d, "
"action=%d, edgeFlags=%d, metaState=%d, xOffset=%f, yOffset=%f, "
"xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, "
"pointerCount=%d",
mChannel->getName().string(),
deviceId, nature, action, edgeFlags, metaState, xOffset, yOffset,
xPrecision, yPrecision, downTime, eventTime, pointerCount);
#endif
if (pointerCount > MAX_POINTERS || pointerCount < 1) {
LOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.",
mChannel->getName().string(), pointerCount);
return BAD_VALUE;
}
status_t result = publishInputEvent(INPUT_EVENT_TYPE_MOTION, deviceId, nature);
if (result < 0) {
return result;
}
mSharedMessage->motion.action = action;
mSharedMessage->motion.edgeFlags = edgeFlags;
mSharedMessage->motion.metaState = metaState;
mSharedMessage->motion.xOffset = xOffset;
mSharedMessage->motion.yOffset = yOffset;
mSharedMessage->motion.xPrecision = xPrecision;
mSharedMessage->motion.yPrecision = yPrecision;
mSharedMessage->motion.downTime = downTime;
mSharedMessage->motion.pointerCount = pointerCount;
mSharedMessage->motion.sampleCount = 1;
mSharedMessage->motion.sampleData[0].eventTime = eventTime;
for (size_t i = 0; i < pointerCount; i++) {
mSharedMessage->motion.pointerIds[i] = pointerIds[i];
mSharedMessage->motion.sampleData[0].coords[i] = pointerCoords[i];
}
// Cache essential information about the motion event to ensure that a malicious consumer
// cannot confuse the publisher by modifying the contents of the shared memory buffer while
// it is being updated.
if (action == MOTION_EVENT_ACTION_MOVE) {
mMotionEventPointerCount = pointerCount;
mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount);
mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement(
mSharedMessage->motion.sampleData, mMotionEventSampleDataStride);
} else {
mMotionEventSampleDataTail = NULL;
}
return OK;
}
status_t InputPublisher::appendMotionSample(
nsecs_t eventTime,
const PointerCoords* pointerCoords) {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld",
mChannel->getName().string(), eventTime);
#endif
if (! mPinned || ! mMotionEventSampleDataTail) {
LOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current "
"MOTION_EVENT_ACTION_MOVE event.", mChannel->getName().string());
return INVALID_OPERATION;
}
InputMessage::SampleData* newTail = InputMessage::sampleDataPtrIncrement(
mMotionEventSampleDataTail, mMotionEventSampleDataStride);
size_t newBytesUsed = reinterpret_cast<char*>(newTail) -
reinterpret_cast<char*>(mSharedMessage);
if (newBytesUsed > mAshmemSize) {
LOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
"buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d",
mChannel->getName().string(),
mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
return NO_MEMORY;
}
int result;
if (mWasDispatched) {
result = sem_trywait(& mSharedMessage->semaphore);
if (result < 0) {
if (errno == EAGAIN) {
// Only possible source of contention is the consumer having consumed (or being in the
// process of consuming) the message and left the semaphore count at 0.
LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
"already been consumed.", mChannel->getName().string());
return FAILED_TRANSACTION;
} else {
LOGE("channel '%s' publisher ~ Error %d in sem_trywait.",
mChannel->getName().string(), errno);
return UNKNOWN_ERROR;
}
}
}
mMotionEventSampleDataTail->eventTime = eventTime;
for (size_t i = 0; i < mMotionEventPointerCount; i++) {
mMotionEventSampleDataTail->coords[i] = pointerCoords[i];
}
mMotionEventSampleDataTail = newTail;
mSharedMessage->motion.sampleCount += 1;
if (mWasDispatched) {
result = sem_post(& mSharedMessage->semaphore);
if (result < 0) {
LOGE("channel '%s' publisher ~ Error %d in sem_post.",
mChannel->getName().string(), errno);
return UNKNOWN_ERROR;
}
}
return OK;
}
status_t InputPublisher::sendDispatchSignal() {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ sendDispatchSignal",
mChannel->getName().string());
#endif
mWasDispatched = true;
return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH);
}
status_t InputPublisher::receiveFinishedSignal() {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ receiveFinishedSignal",
mChannel->getName().string());
#endif
char signal;
status_t result = mChannel->receiveSignal(& signal);
if (result) {
return result;
}
if (signal != INPUT_SIGNAL_FINISHED) {
LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer",
mChannel->getName().string(), signal);
return UNKNOWN_ERROR;
}
return OK;
}
// --- InputConsumer ---
InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
mChannel(channel), mSharedMessage(NULL) {
}
InputConsumer::~InputConsumer() {
if (mSharedMessage) {
munmap(mSharedMessage, mAshmemSize);
}
}
status_t InputConsumer::initialize() {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' consumer ~ initialize",
mChannel->getName().string());
#endif
int ashmemFd = mChannel->getAshmemFd();
int result = ashmem_get_size_region(ashmemFd);
if (result < 0) {
LOGE("channel '%s' consumer ~ Error %d getting size of ashmem fd %d.",
mChannel->getName().string(), result, ashmemFd);
return UNKNOWN_ERROR;
}
mAshmemSize = (size_t) result;
mSharedMessage = static_cast<InputMessage*>(mmap(NULL, mAshmemSize,
PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0));
if (! mSharedMessage) {
LOGE("channel '%s' consumer ~ mmap failed on ashmem fd %d.",
mChannel->getName().string(), ashmemFd);
return NO_MEMORY;
}
return OK;
}
status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** event) {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' consumer ~ consume",
mChannel->getName().string());
#endif
*event = NULL;
int ashmemFd = mChannel->getAshmemFd();
int result = ashmem_pin_region(ashmemFd, 0, 0);
if (result != ASHMEM_NOT_PURGED) {
if (result == ASHMEM_WAS_PURGED) {
LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged "
"which probably indicates that the publisher and consumer are out of sync.",
mChannel->getName().string(), result, ashmemFd);
return INVALID_OPERATION;
}
LOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.",
mChannel->getName().string(), result, ashmemFd);
return UNKNOWN_ERROR;
}
if (mSharedMessage->consumed) {
LOGE("channel '%s' consumer ~ The current message has already been consumed.",
mChannel->getName().string());
return INVALID_OPERATION;
}
// Acquire but *never release* the semaphore. Contention on the semaphore is used to signal
// to the publisher that the message has been consumed (or is in the process of being
// consumed). Eventually the publisher will reinitialize the semaphore for the next message.
result = sem_wait(& mSharedMessage->semaphore);
if (result < 0) {
LOGE("channel '%s' consumer ~ Error %d in sem_wait.",
mChannel->getName().string(), errno);
return UNKNOWN_ERROR;
}
mSharedMessage->consumed = true;
switch (mSharedMessage->type) {
case INPUT_EVENT_TYPE_KEY: {
KeyEvent* keyEvent = factory->createKeyEvent();
if (! keyEvent) return NO_MEMORY;
populateKeyEvent(keyEvent);
*event = keyEvent;
break;
}
case INPUT_EVENT_TYPE_MOTION: {
MotionEvent* motionEvent = factory->createMotionEvent();
if (! motionEvent) return NO_MEMORY;
populateMotionEvent(motionEvent);
*event = motionEvent;
break;
}
default:
LOGE("channel '%s' consumer ~ Received message of unknown type %d",
mChannel->getName().string(), mSharedMessage->type);
return UNKNOWN_ERROR;
}
return OK;
}
status_t InputConsumer::sendFinishedSignal() {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' consumer ~ sendFinishedSignal",
mChannel->getName().string());
#endif
return mChannel->sendSignal(INPUT_SIGNAL_FINISHED);
}
status_t InputConsumer::receiveDispatchSignal() {
#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' consumer ~ receiveDispatchSignal",
mChannel->getName().string());
#endif
char signal;
status_t result = mChannel->receiveSignal(& signal);
if (result) {
return result;
}
if (signal != INPUT_SIGNAL_DISPATCH) {
LOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher",
mChannel->getName().string(), signal);
return UNKNOWN_ERROR;
}
return OK;
}
void InputConsumer::populateKeyEvent(KeyEvent* keyEvent) const {
keyEvent->initialize(
mSharedMessage->deviceId,
mSharedMessage->nature,
mSharedMessage->key.action,
mSharedMessage->key.flags,
mSharedMessage->key.keyCode,
mSharedMessage->key.scanCode,
mSharedMessage->key.metaState,
mSharedMessage->key.repeatCount,
mSharedMessage->key.downTime,
mSharedMessage->key.eventTime);
}
void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const {
motionEvent->initialize(
mSharedMessage->deviceId,
mSharedMessage->nature,
mSharedMessage->motion.action,
mSharedMessage->motion.edgeFlags,
mSharedMessage->motion.metaState,
mSharedMessage->motion.sampleData[0].coords[0].x,
mSharedMessage->motion.sampleData[0].coords[0].y,
mSharedMessage->motion.xPrecision,
mSharedMessage->motion.yPrecision,
mSharedMessage->motion.downTime,
mSharedMessage->motion.sampleData[0].eventTime,
mSharedMessage->motion.pointerCount,
mSharedMessage->motion.pointerIds,
mSharedMessage->motion.sampleData[0].coords);
size_t sampleCount = mSharedMessage->motion.sampleCount;
if (sampleCount > 1) {
InputMessage::SampleData* sampleData = mSharedMessage->motion.sampleData;
size_t sampleDataStride = InputMessage::sampleDataStride(
mSharedMessage->motion.pointerCount);
while (--sampleCount > 0) {
sampleData = InputMessage::sampleDataPtrIncrement(sampleData, sampleDataStride);
motionEvent->addSample(sampleData->eventTime, sampleData->coords);
}
}
motionEvent->offsetLocation(mSharedMessage->motion.xOffset,
mSharedMessage->motion.yOffset);
}
} // namespace android

View File

@@ -1,16 +1,38 @@
# Build the unit tests.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
region.cpp
test_src_files := \
InputDispatcher_test.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libui
libEGL \
libbinder \
libpixelflinger \
libhardware \
libhardware_legacy \
libui \
libstlport
LOCAL_MODULE:= test-region
LOCAL_STATIC_LIBRARIES := \
libgtest \
libgtest_main
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES := \
bionic \
bionic/libstdc++/include \
external/gtest/include \
external/stlport/stlport
include $(BUILD_EXECUTABLE)
LOCAL_MODULE_TAGS := eng tests
$(foreach file,$(test_src_files), \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
$(eval include $(BUILD_EXECUTABLE)) \
)
# Build the manual test programs.
include $(call all-subdir-makefiles)

View File

@@ -0,0 +1,19 @@
//
// Copyright 2010 The Android Open Source Project
//
#include <ui/InputDispatcher.h>
#include <gtest/gtest.h>
namespace android {
class InputDispatcherTest : public testing::Test {
public:
};
TEST_F(InputDispatcherTest, Dummy) {
SCOPED_TRACE("Trace");
ASSERT_FALSE(true);
}
} // namespace android

View File

@@ -0,0 +1,16 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
region.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libui
LOCAL_MODULE:= test-region
LOCAL_MODULE_TAGS := tests
include $(BUILD_EXECUTABLE)

View File

@@ -26,6 +26,8 @@ commonSources:= \
Debug.cpp \
FileMap.cpp \
Flattenable.cpp \
PollLoop.cpp \
Pool.cpp \
RefBase.cpp \
ResourceTypes.cpp \
SharedBuffer.cpp \

267
libs/utils/PollLoop.cpp Normal file
View File

@@ -0,0 +1,267 @@
//
// Copyright 2010 The Android Open Source Project
//
// A select loop implementation.
//
#define LOG_TAG "PollLoop"
//#define LOG_NDEBUG 0
// Debugs poll and wake interactions.
#define DEBUG_POLL_AND_WAKE 0
// Debugs callback registration and invocation.
#define DEBUG_CALLBACKS 1
#include <cutils/log.h>
#include <utils/PollLoop.h>
#include <unistd.h>
#include <fcntl.h>
namespace android {
PollLoop::PollLoop() :
mPolling(false) {
openWakePipe();
}
PollLoop::~PollLoop() {
closeWakePipe();
}
void PollLoop::openWakePipe() {
int wakeFds[2];
int result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
// Add the wake pipe to the head of the request list with a null callback.
struct pollfd requestedFd;
requestedFd.fd = mWakeReadPipeFd;
requestedFd.events = POLLIN;
mRequestedFds.insertAt(requestedFd, 0);
RequestedCallback requestedCallback;
requestedCallback.callback = NULL;
requestedCallback.data = NULL;
mRequestedCallbacks.insertAt(requestedCallback, 0);
}
void PollLoop::closeWakePipe() {
close(mWakeReadPipeFd);
close(mWakeWritePipeFd);
// Note: We don't need to remove the poll structure or callback entry because this
// method is currently only called by the destructor.
}
bool PollLoop::pollOnce(int timeoutMillis) {
mLock.lock();
mPolling = true;
mLock.unlock();
bool result;
size_t requestedCount = mRequestedFds.size();
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - waiting on %d fds", this, requestedCount);
for (size_t i = 0; i < requestedCount; i++) {
LOGD(" fd %d - events %d", mRequestedFds[i].fd, mRequestedFds[i].events);
}
#endif
int respondedCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
if (respondedCount == 0) {
// Timeout
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - timeout", this);
#endif
result = false;
goto Done;
}
if (respondedCount < 0) {
// Error
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - error, errno=%d", this, errno);
#endif
if (errno != EINTR) {
LOGW("Poll failed with an unexpected error, errno=%d", errno);
}
result = false;
goto Done;
}
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount);
for (size_t i = 0; i < requestedCount; i++) {
LOGD(" fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events,
mRequestedFds[i].revents);
}
#endif
mPendingCallbacks.clear();
for (size_t i = 0; i < requestedCount; i++) {
const struct pollfd& requestedFd = mRequestedFds.itemAt(i);
short revents = requestedFd.revents;
if (revents) {
const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i);
Callback callback = requestedCallback.callback;
if (callback) {
PendingCallback pendingCallback;
pendingCallback.fd = requestedFd.fd;
pendingCallback.events = requestedFd.revents;
pendingCallback.callback = callback;
pendingCallback.data = requestedCallback.data;
mPendingCallbacks.push(pendingCallback);
} else {
if (requestedFd.fd == mWakeReadPipeFd) {
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - awoken", this);
#endif
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while (nRead == sizeof(buffer));
} else {
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
LOGD("%p ~ pollOnce - fd %d has no callback!", this, requestedFd.fd);
#endif
}
}
respondedCount -= 1;
if (respondedCount == 0) {
break;
}
}
}
result = true;
Done:
mLock.lock();
mPolling = false;
mAwake.broadcast();
mLock.unlock();
if (result) {
size_t pendingCount = mPendingCallbacks.size();
for (size_t i = 0; i < pendingCount; i++) {
const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i);
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd);
#endif
bool keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events,
pendingCallback.data);
if (! keep) {
removeCallback(pendingCallback.fd);
}
}
}
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ pollOnce - done", this);
#endif
return result;
}
void PollLoop::wake() {
#if DEBUG_POLL_AND_WAKE
LOGD("%p ~ wake", this);
#endif
ssize_t nWrite = write(mWakeWritePipeFd, "W", 1);
if (nWrite != 1) {
if (errno != EAGAIN) {
LOGW("Could not write wake signal, errno=%d", errno);
}
}
}
void PollLoop::setCallback(int fd, int events, Callback callback, void* data) {
#if DEBUG_CALLBACKS
LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events);
#endif
if (! events || ! callback) {
LOGE("Invalid attempt to set a callback with no selected poll events or no callback.");
removeCallback(fd);
return;
}
wakeAndLock();
struct pollfd requestedFd;
requestedFd.fd = fd;
requestedFd.events = events;
RequestedCallback requestedCallback;
requestedCallback.callback = callback;
requestedCallback.data = data;
ssize_t index = getRequestIndexLocked(fd);
if (index < 0) {
mRequestedFds.push(requestedFd);
mRequestedCallbacks.push(requestedCallback);
} else {
mRequestedFds.replaceAt(requestedFd, size_t(index));
mRequestedCallbacks.replaceAt(requestedCallback, size_t(index));
}
mLock.unlock();
}
bool PollLoop::removeCallback(int fd) {
#if DEBUG_CALLBACKS
LOGD("%p ~ removeCallback - fd=%d", this, fd);
#endif
wakeAndLock();
ssize_t index = getRequestIndexLocked(fd);
if (index >= 0) {
mRequestedFds.removeAt(size_t(index));
mRequestedCallbacks.removeAt(size_t(index));
}
mLock.unlock();
return index >= 0;
}
ssize_t PollLoop::getRequestIndexLocked(int fd) {
size_t requestCount = mRequestedFds.size();
for (size_t i = 0; i < requestCount; i++) {
if (mRequestedFds.itemAt(i).fd == fd) {
return i;
}
}
return -1;
}
void PollLoop::wakeAndLock() {
mLock.lock();
while (mPolling) {
wake();
mAwake.wait(mLock);
}
}
} // namespace android

37
libs/utils/Pool.cpp Normal file
View File

@@ -0,0 +1,37 @@
//
// Copyright 2010 The Android Open Source Project
//
// A simple memory pool.
//
#define LOG_TAG "Pool"
//#define LOG_NDEBUG 0
#include <cutils/log.h>
#include <utils/Pool.h>
#include <stdlib.h>
namespace android {
// TODO Provide a real implementation of a pool. This is just a stub for initial development.
PoolImpl::PoolImpl(size_t objSize) :
mObjSize(objSize) {
}
PoolImpl::~PoolImpl() {
}
void* PoolImpl::allocImpl() {
void* ptr = malloc(mObjSize);
LOG_ALWAYS_FATAL_IF(ptr == NULL, "Cannot allocate new pool object.");
return ptr;
}
void PoolImpl::freeImpl(void* obj) {
LOG_ALWAYS_FATAL_IF(obj == NULL, "Caller attempted to free NULL pool object.");
return free(obj);
}
} // namespace android

View File

@@ -30,10 +30,9 @@ namespace android {
StopWatch::StopWatch(const char *name, int clock, uint32_t flags)
: mName(name), mClock(clock), mFlags(flags),
mStartTime(0), mNumLaps(0)
: mName(name), mClock(clock), mFlags(flags)
{
mStartTime = systemTime(mClock);
reset();
}
StopWatch::~StopWatch()
@@ -72,6 +71,12 @@ nsecs_t StopWatch::elapsedTime() const
return systemTime(mClock) - mStartTime;
}
void StopWatch::reset()
{
mNumLaps = 0;
mStartTime = systemTime(mClock);
}
/*****************************************************************************/

View File

@@ -108,13 +108,7 @@ size_t VectorImpl::capacity() const
ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
{
if (index > size())
return BAD_INDEX;
void* where = _grow(index, vector.size());
if (where) {
_do_copy(where, vector.arrayImpl(), vector.size());
}
return where ? index : (ssize_t)NO_MEMORY;
return insertAt(vector.arrayImpl(), index, vector.size());
}
ssize_t VectorImpl::appendVector(const VectorImpl& vector)
@@ -226,9 +220,9 @@ ssize_t VectorImpl::add()
return add(0);
}
ssize_t VectorImpl::add(const void* item)
ssize_t VectorImpl::add(const void* item, size_t numItems)
{
return insertAt(item, size());
return insertAt(item, size(), numItems);
}
ssize_t VectorImpl::replaceAt(size_t index)

View File

@@ -0,0 +1,33 @@
# Build the unit tests.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
test_src_files := \
PollLoop_test.cpp
LOCAL_SHARED_LIBRARIES := \
libz \
liblog \
libcutils \
libutils \
libstlport
LOCAL_STATIC_LIBRARIES := \
libgtest \
libgtest_main
LOCAL_C_INCLUDES := \
external/zlib \
external/icu4c/common \
bionic \
bionic/libstdc++/include \
external/gtest/include \
external/stlport/stlport
LOCAL_MODULE_TAGS := eng tests
$(foreach file,$(test_src_files), \
$(eval LOCAL_SRC_FILES := $(file)) \
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
$(eval include $(BUILD_EXECUTABLE)) \
)

View File

@@ -0,0 +1,398 @@
//
// Copyright 2010 The Android Open Source Project
//
#include <utils/PollLoop.h>
#include <utils/Timers.h>
#include <utils/StopWatch.h>
#include <gtest/gtest.h>
#include <unistd.h>
#include <time.h>
#include "TestHelpers.h"
// # of milliseconds to fudge stopwatch measurements
#define TIMING_TOLERANCE_MS 25
namespace android {
class Pipe {
public:
int sendFd;
int receiveFd;
Pipe() {
int fds[2];
::pipe(fds);
receiveFd = fds[0];
sendFd = fds[1];
}
~Pipe() {
::close(sendFd);
::close(receiveFd);
}
bool writeSignal() {
return ::write(sendFd, "*", 1) == 1;
}
bool readSignal() {
char buf[1];
return ::read(receiveFd, buf, 1) == 1;
}
};
class DelayedWake : public DelayedTask {
sp<PollLoop> mPollLoop;
public:
DelayedWake(int delayMillis, const sp<PollLoop> pollLoop) :
DelayedTask(delayMillis), mPollLoop(pollLoop) {
}
protected:
virtual void doTask() {
mPollLoop->wake();
}
};
class DelayedWriteSignal : public DelayedTask {
Pipe* mPipe;
public:
DelayedWriteSignal(int delayMillis, Pipe* pipe) :
DelayedTask(delayMillis), mPipe(pipe) {
}
protected:
virtual void doTask() {
mPipe->writeSignal();
}
};
class CallbackHandler {
public:
void setCallback(const sp<PollLoop>& pollLoop, int fd, int events) {
pollLoop->setCallback(fd, events, staticHandler, this);
}
protected:
virtual ~CallbackHandler() { }
virtual bool handler(int fd, int events) = 0;
private:
static bool staticHandler(int fd, int events, void* data) {
return static_cast<CallbackHandler*>(data)->handler(fd, events);
}
};
class StubCallbackHandler : public CallbackHandler {
public:
bool nextResult;
int callbackCount;
int fd;
int events;
StubCallbackHandler(bool nextResult) : nextResult(nextResult),
callbackCount(0), fd(-1), events(-1) {
}
protected:
virtual bool handler(int fd, int events) {
callbackCount += 1;
this->fd = fd;
this->events = events;
return nextResult;
}
};
class PollLoopTest : public testing::Test {
protected:
sp<PollLoop> mPollLoop;
virtual void SetUp() {
mPollLoop = new PollLoop();
}
virtual void TearDown() {
mPollLoop.clear();
}
};
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) {
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal timeout";
EXPECT_FALSE(result)
<< "pollOnce result should be false because timeout occurred";
}
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) {
mPollLoop->wake();
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(1000);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. zero because wake() was called before waiting";
EXPECT_TRUE(result)
<< "pollOnce result should be true because loop was awoken";
}
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) {
sp<DelayedWake> delayedWake = new DelayedWake(100, mPollLoop);
delayedWake->run();
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(1000);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal wake delay";
EXPECT_TRUE(result)
<< "pollOnce result should be true because loop was awoken";
}
TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) {
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(0);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should be approx. zero";
EXPECT_FALSE(result)
<< "pollOnce result should be false because timeout occurred";
}
TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) {
Pipe pipe;
StubCallbackHandler handler(true);
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(0);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should be approx. zero";
EXPECT_FALSE(result)
<< "pollOnce result should be false because timeout occurred";
EXPECT_EQ(0, handler.callbackCount)
<< "callback should not have been invoked because FD was not signalled";
}
TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturnsTrue) {
Pipe pipe;
StubCallbackHandler handler(true);
ASSERT_TRUE(pipe.writeSignal());
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(0);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should be approx. zero";
EXPECT_TRUE(result)
<< "pollOnce result should be true because FD was signalled";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should be invoked exactly once";
EXPECT_EQ(pipe.receiveFd, handler.fd)
<< "callback should have received pipe fd as parameter";
EXPECT_EQ(POLL_IN, handler.events)
<< "callback should have received POLL_IN as events";
}
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturnsFalse) {
Pipe pipe;
StubCallbackHandler handler(true);
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal timeout";
EXPECT_FALSE(result)
<< "pollOnce result should be false because timeout occurred";
EXPECT_EQ(0, handler.callbackCount)
<< "callback should not have been invoked because FD was not signalled";
}
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturnsTrue) {
Pipe pipe;
StubCallbackHandler handler(true);
pipe.writeSignal();
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_TRUE(pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should be approx. zero";
EXPECT_TRUE(result)
<< "pollOnce result should be true because FD was signalled";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should be invoked exactly once";
EXPECT_EQ(pipe.receiveFd, handler.fd)
<< "callback should have received pipe fd as parameter";
EXPECT_EQ(POLL_IN, handler.events)
<< "callback should have received POLL_IN as events";
}
TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturnsTrue) {
Pipe pipe;
StubCallbackHandler handler(true);
sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
delayedWriteSignal->run();
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(1000);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_TRUE(pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal signal delay";
EXPECT_TRUE(result)
<< "pollOnce result should be true because FD was signalled";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should be invoked exactly once";
EXPECT_EQ(pipe.receiveFd, handler.fd)
<< "callback should have received pipe fd as parameter";
EXPECT_EQ(POLL_IN, handler.events)
<< "callback should have received POLL_IN as events";
}
TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {
Pipe pipe;
StubCallbackHandler handler(true);
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
pipe.writeSignal(); // would cause FD to be considered signalled
mPollLoop->removeCallback(pipe.receiveFd);
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_TRUE(pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal timeout because FD was no longer registered";
EXPECT_FALSE(result)
<< "pollOnce result should be false because timeout occurred";
EXPECT_EQ(0, handler.callbackCount)
<< "callback should not be invoked";
}
TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {
Pipe pipe;
StubCallbackHandler handler(false);
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
// First loop: Callback is registered and FD is signalled.
pipe.writeSignal();
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(0);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_TRUE(pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal zero because FD was already signalled";
EXPECT_TRUE(result)
<< "pollOnce result should be true because FD was signalled";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should be invoked";
// Second loop: Callback is no longer registered and FD is signalled.
pipe.writeSignal();
stopWatch.reset();
result = mPollLoop->pollOnce(0);
elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_TRUE(pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. equal zero because timeout was zero";
EXPECT_FALSE(result)
<< "pollOnce result should be false because timeout occurred";
EXPECT_EQ(1, handler.callbackCount)
<< "callback should not be invoked this time";
}
TEST_F(PollLoopTest, RemoveCallback_WhenCallbackNotAdded_ReturnsFalse) {
bool result = mPollLoop->removeCallback(1);
EXPECT_FALSE(result)
<< "removeCallback should return false because FD not registered";
}
TEST_F(PollLoopTest, RemoveCallback_WhenCallbackAddedThenRemovedTwice_ReturnsTrueFirstTimeAndReturnsFalseSecondTime) {
Pipe pipe;
StubCallbackHandler handler(false);
handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
// First time.
bool result = mPollLoop->removeCallback(pipe.receiveFd);
EXPECT_TRUE(result)
<< "removeCallback should return true first time because FD was registered";
// Second time.
result = mPollLoop->removeCallback(pipe.receiveFd);
EXPECT_FALSE(result)
<< "removeCallback should return false second time because FD was no longer registered";
}
TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {
Pipe pipe;
StubCallbackHandler handler1(true);
StubCallbackHandler handler2(true);
handler1.setCallback(mPollLoop, pipe.receiveFd, POLL_IN);
handler2.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); // replace it
pipe.writeSignal(); // would cause FD to be considered signalled
StopWatch stopWatch("pollOnce");
bool result = mPollLoop->pollOnce(100);
int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
ASSERT_TRUE(pipe.readSignal())
<< "signal should actually have been written";
EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
<< "elapsed time should approx. zero because FD was already signalled";
EXPECT_TRUE(result)
<< "pollOnce result should be true because FD was signalled";
EXPECT_EQ(0, handler1.callbackCount)
<< "original handler callback should not be invoked because it was replaced";
EXPECT_EQ(1, handler2.callbackCount)
<< "replacement handler callback should be invoked";
}
} // namespace android

View File

@@ -0,0 +1,44 @@
/*
* 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 TESTHELPERS_H
#define TESTHELPERS_H
#include <utils/threads.h>
namespace android {
class DelayedTask : public Thread {
int mDelayMillis;
public:
DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
protected:
virtual ~DelayedTask() { }
virtual void doTask() = 0;
virtual bool threadLoop() {
usleep(mDelayMillis * 1000);
doTask();
return false;
}
};
} // namespace android
#endif // TESTHELPERS_H

View File

@@ -0,0 +1,500 @@
/*
* 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_INPUT_H
#define _ANDROID_INPUT_H
/******************************************************************
*
* IMPORTANT NOTICE:
*
* This file is part of Android's set of stable system headers
* exposed by the Android NDK (Native Development Kit).
*
* Third-party source AND binary code relies on the definitions
* here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
*
* - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
* - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
* - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
/*
* Structures and functions to receive and process input events in
* native code.
*
* NOTE: These functions MUST be implemented by /system/lib/libui.so
*/
#include <sys/types.h>
#include <android/keycodes.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Input device classes.
*/
enum {
/* The input device is a keyboard. */
INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002,
/* The input device is a touchscreen (either single-touch or multi-touch). */
INPUT_DEVICE_CLASS_TOUCHSCREEN = 0x00000004,
/* The input device is a trackball. */
INPUT_DEVICE_CLASS_TRACKBALL = 0x00000008,
/* The input device is a multi-touch touchscreen. */
INPUT_DEVICE_CLASS_TOUCHSCREEN_MT= 0x00000010,
/* The input device is a directional pad. */
INPUT_DEVICE_CLASS_DPAD = 0x00000020
};
/*
* Key states (may be returned by queries about the current state of a
* particular key code, scan code or switch).
*
* XXX should we call this BUTTON_STATE_XXX?
*/
enum {
/* The key state is unknown or the requested key itself is not supported. */
KEY_STATE_UNKNOWN = -1,
/* The key is up. */
KEY_STATE_UP = 0,
/* The key is down. */
KEY_STATE_DOWN = 1,
/* The key is down but is a virtual key press that is being emulated by the system. */
KEY_STATE_VIRTUAL = 2
};
/*
* Meta key / modifer state.
*/
enum {
/* No meta keys are pressed. */
META_NONE = 0,
/* This mask is used to check whether one of the ALT meta keys is pressed. */
META_ALT_ON = 0x02,
/* This mask is used to check whether the left ALT meta key is pressed. */
META_ALT_LEFT_ON = 0x10,
/* This mask is used to check whether the right ALT meta key is pressed. */
META_ALT_RIGHT_ON = 0x20,
/* This mask is used to check whether one of the SHIFT meta keys is pressed. */
META_SHIFT_ON = 0x01,
/* This mask is used to check whether the left SHIFT meta key is pressed. */
META_SHIFT_LEFT_ON = 0x40,
/* This mask is used to check whether the right SHIFT meta key is pressed. */
META_SHIFT_RIGHT_ON = 0x80,
/* This mask is used to check whether the SYM meta key is pressed. */
META_SYM_ON = 0x04
};
/*
* Input events.
*
* Input events are opaque structures. Use the provided accessors functions to
* read their properties.
*/
struct input_event_t;
typedef struct input_event_t input_event_t;
/*
* Input event types.
*/
enum {
/* Indicates that the input event is a key event. */
INPUT_EVENT_TYPE_KEY = 1,
/* Indicates that the input event is a motion event. */
INPUT_EVENT_TYPE_MOTION = 2
};
/*
* Key event actions.
*/
enum {
/* The key has been pressed down. */
KEY_EVENT_ACTION_DOWN = 0,
/* The key has been released. */
KEY_EVENT_ACTION_UP = 1,
/* Multiple duplicate key events have occurred in a row, or a complex string is
* being delivered. The repeat_count property of the key event contains the number
* of times the given key code should be executed.
*/
KEY_EVENT_ACTION_MULTIPLE = 2
};
/*
* Key event flags.
*/
enum {
/* This mask is set if the device woke because of this key event. */
KEY_EVENT_FLAG_WOKE_HERE = 0x1,
/* This mask is set if the key event was generated by a software keyboard. */
KEY_EVENT_FLAG_SOFT_KEYBOARD = 0x2,
/* This mask is set if we don't want the key event to cause us to leave touch mode. */
KEY_EVENT_FLAG_KEEP_TOUCH_MODE = 0x4,
/* This mask is set if an event was known to come from a trusted part
* of the system. That is, the event is known to come from the user,
* and could not have been spoofed by a third party component. */
KEY_EVENT_FLAG_FROM_SYSTEM = 0x8,
/* This mask is used for compatibility, to identify enter keys that are
* coming from an IME whose enter key has been auto-labelled "next" or
* "done". This allows TextView to dispatch these as normal enter keys
* for old applications, but still do the appropriate action when
* receiving them. */
KEY_EVENT_FLAG_EDITOR_ACTION = 0x10,
/* When associated with up key events, this indicates that the key press
* has been canceled. Typically this is used with virtual touch screen
* keys, where the user can slide from the virtual key area on to the
* display: in that case, the application will receive a canceled up
* event and should not perform the action normally associated with the
* key. Note that for this to work, the application can not perform an
* action for a key until it receives an up or the long press timeout has
* expired. */
KEY_EVENT_FLAG_CANCELED = 0x20,
/* This key event was generated by a virtual (on-screen) hard key area.
* Typically this is an area of the touchscreen, outside of the regular
* display, dedicated to "hardware" buttons. */
KEY_EVENT_FLAG_VIRTUAL_HARD_KEY = 0x40,
/* This flag is set for the first key repeat that occurs after the
* long press timeout. */
KEY_EVENT_FLAG_LONG_PRESS = 0x80,
/* Set when a key event has KEY_EVENT_FLAG_CANCELED set because a long
* press action was executed while it was down. */
KEY_EVENT_FLAG_CANCELED_LONG_PRESS = 0x100,
/* Set for KEY_EVENT_ACTION_UP when this event's key code is still being
* tracked from its initial down. That is, somebody requested that tracking
* started on the key down and a long press has not caused
* the tracking to be canceled. */
KEY_EVENT_FLAG_TRACKING = 0x200
};
/*
* Motion event actions.
*/
/* Bit shift for the action bits holding the pointer index as
* defined by MOTION_EVENT_ACTION_POINTER_INDEX_MASK.
*/
#define MOTION_EVENT_ACTION_POINTER_INDEX_SHIFT 8
enum {
/* Bit mask of the parts of the action code that are the action itself.
*/
MOTION_EVENT_ACTION_MASK = 0xff,
/* Bits in the action code that represent a pointer index, used with
* MOTION_EVENT_ACTION_POINTER_DOWN and MOTION_EVENT_ACTION_POINTER_UP. Shifting
* down by MOTION_EVENT_ACTION_POINTER_INDEX_SHIFT provides the actual pointer
* index where the data for the pointer going up or down can be found.
*/
MOTION_EVENT_ACTION_POINTER_INDEX_MASK = 0xff00,
/* A pressed gesture has started, the motion contains the initial starting location.
*/
MOTION_EVENT_ACTION_DOWN = 0,
/* A pressed gesture has finished, the motion contains the final release location
* as well as any intermediate points since the last down or move event.
*/
MOTION_EVENT_ACTION_UP = 1,
/* A change has happened during a press gesture (between MOTION_EVENT_ACTION_DOWN and
* MOTION_EVENT_ACTION_UP). The motion contains the most recent point, as well as
* any intermediate points since the last down or move event.
*/
MOTION_EVENT_ACTION_MOVE = 2,
/* The current gesture has been aborted.
* You will not receive any more points in it. You should treat this as
* an up event, but not perform any action that you normally would.
*/
MOTION_EVENT_ACTION_CANCEL = 3,
/* A movement has happened outside of the normal bounds of the UI element.
* This does not provide a full gesture, but only the initial location of the movement/touch.
*/
MOTION_EVENT_ACTION_OUTSIDE = 4,
/* A non-primary pointer has gone down.
* The bits in MOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
*/
MOTION_EVENT_ACTION_POINTER_DOWN = 5,
/* A non-primary pointer has gone up.
* The bits in MOTION_EVENT_ACTION_POINTER_INDEX_MASK indicate which pointer changed.
*/
MOTION_EVENT_ACTION_POINTER_UP = 6
};
/*
* Motion event edge touch flags.
*/
enum {
/* No edges intersected */
MOTION_EVENT_EDGE_FLAG_NONE = 0,
/* Flag indicating the motion event intersected the top edge of the screen. */
MOTION_EVENT_EDGE_FLAG_TOP = 0x01,
/* Flag indicating the motion event intersected the bottom edge of the screen. */
MOTION_EVENT_EDGE_FLAG_BOTTOM = 0x02,
/* Flag indicating the motion event intersected the left edge of the screen. */
MOTION_EVENT_EDGE_FLAG_LEFT = 0x04,
/* Flag indicating the motion event intersected the right edge of the screen. */
MOTION_EVENT_EDGE_FLAG_RIGHT = 0x08
};
/*
* Specifies the logical nature of an input event.
* For example, the nature distinguishes between motion events that represent touches and
* those that represent trackball moves.
*
* XXX This concept is tentative. Another idea would be to associate events with logical
* controllers rather than physical devices. The interpretation of an event would
* be made with respect to the nature of the controller that is considered the logical
* source of an event. The decoupling is beneficial since multiple physical (and virtual)
* devices could be responsible for producing events that would be associated with
* various logical controllers. For example, the hard keyboard, on screen keyboard,
* and peripheral keyboard could be mapped onto a single logical "keyboard" controller
* (or treated independently, if desired).
*/
enum {
INPUT_EVENT_NATURE_KEY = 1,
INPUT_EVENT_NATURE_TOUCH = 2,
INPUT_EVENT_NATURE_TRACKBALL = 3
};
/*
* Input event accessors.
*
* Note that most functions can only be used on input events that are of a given type.
* Calling these functions on input events of other types will yield undefined behavior.
*/
/*** Accessors for all input events. ***/
/* Get the input event type. */
int32_t input_event_get_type(const input_event_t* event);
/* Get the id for the device that an input event came from.
*
* Input events can be generated by multiple different input devices.
* Use the input device id to obtain information about the input
* device that was responsible for generating a particular event.
*
* An input device id of 0 indicates that the event didn't come from a physical device;
* other numbers are arbitrary and you shouldn't depend on the values.
* Use the provided input device query API to obtain information about input devices.
*/
int32_t input_event_get_device_id(const input_event_t* event);
/* Get the input event nature. */
int32_t input_event_get_nature(const input_event_t* event);
/*** Accessors for key events only. ***/
/* Get the key event action. */
int32_t key_event_get_action(const input_event_t* key_event);
/* Get the key event flags. */
int32_t key_event_get_flags(const input_event_t* key_event);
/* Get the key code of the key event.
* This is the physical key that was pressed, not the Unicode character. */
int32_t key_event_get_key_code(const input_event_t* key_event);
/* Get the hardware key id of this key event.
* These values are not reliable and vary from device to device. */
int32_t key_event_get_scan_code(const input_event_t* key_event);
/* Get the meta key state. */
int32_t key_event_get_meta_state(const input_event_t* key_event);
/* Get the repeat count of the event.
* For both key up an key down events, this is the number of times the key has
* repeated with the first down starting at 0 and counting up from there. For
* multiple key events, this is the number of down/up pairs that have occurred. */
int32_t key_event_get_repeat_count(const input_event_t* key_event);
/* Get the time of the most recent key down event, in the
* java.lang.System.nanoTime() time base. If this is a down event,
* this will be the same as eventTime.
* Note that when chording keys, this value is the down time of the most recently
* pressed key, which may not be the same physical key of this event. */
int64_t key_event_get_down_time(const input_event_t* key_event);
/* Get the time this event occurred, in the
* java.lang.System.nanoTime() time base. */
int64_t key_event_get_event_time(const input_event_t* key_event);
/*** Accessors for motion events only. ***/
/* Get the combined motion event action code and pointer index. */
int32_t motion_event_get_action(const input_event_t* motion_event);
/* Get the state of any meta / modifier keys that were in effect when the
* event was generated. */
int32_t motion_event_get_meta_state(const input_event_t* motion_event);
/* Get a bitfield indicating which edges, if any, were touched by this motion event.
* For touch events, clients can use this to determine if the user's finger was
* touching the edge of the display. */
int32_t motion_event_get_edge_flags(const input_event_t* motion_event);
/* Get the time when the user originally pressed down to start a stream of
* position events, in the java.lang.System.nanoTime() time base. */
int64_t motion_event_get_down_time(const input_event_t* motion_event);
/* Get the time when this specific event was generated,
* in the java.lang.System.nanoTime() time base. */
int64_t motion_event_get_event_time(const input_event_t* motion_event);
/* Get the precision of the X coordinates being reported.
* You can multiply this number with an X coordinate sample to find the
* actual hardware value of the X coordinate. */
float motion_event_get_x_precision(const input_event_t* motion_event);
/* Get the precision of the Y coordinates being reported.
* You can multiply this number with a Y coordinate sample to find the
* actual hardware value of the Y coordinate. */
float motion_event_get_y_precision(const input_event_t* motion_event);
/* Get the number of pointers of data contained in this event.
* Always >= 1. */
size_t motion_event_get_pointer_count(const input_event_t* motion_event);
/* Get the pointer identifier associated with a particular pointer
* data index is this event. The identifier tells you the actual pointer
* number associated with the data, accounting for individual pointers
* going up and down since the start of the current gesture. */
int32_t motion_event_get_pointer_id(const input_event_t* motion_event, size_t pointer_index);
/* Get the original raw X coordinate of this event. For touch
* events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views. */
float motion_event_get_raw_x(const input_event_t* motion_event);
/* Get the original raw X coordinate of this event. For touch
* events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views. */
float motion_event_get_raw_y(const input_event_t* motion_event);
/* Get the current X coordinate of this event for the given pointer index.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index);
/* Get the current Y coordinate of this event for the given pointer index.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float motion_event_get_y(const input_event_t* motion_event, size_t pointer_index);
/* Get the current pressure of this event for the given pointer index.
* The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
* however values higher than 1 may be generated depending on the calibration of
* the input device. */
float motion_event_get_pressure(const input_event_t* motion_event, size_t pointer_index);
/* Get the current scaled value of the approximate size for the given pointer index.
* This represents some approximation of the area of the screen being
* pressed; the actual value in pixels corresponding to the
* touch is normalized with the device specific range of values
* and scaled to a value between 0 and 1. The value of size can be used to
* determine fat touch events. */
float motion_event_get_size(const input_event_t* motion_event, size_t pointer_index);
/* Get the number of historical points in this event. These are movements that
* have occurred between this event and the previous event. This only applies
* to MOTION_EVENT_ACTION_MOVE events -- all other actions will have a size of 0.
* Historical samples are indexed from oldest to newest. */
size_t motion_event_get_history_size(const input_event_t* motion_event);
/* Get the time that a historical movement occurred between this event and
* the previous event, in the java.lang.System.nanoTime() time base. */
int64_t motion_event_get_historical_event_time(input_event_t* motion_event,
size_t history_index);
/* Get the historical X coordinate of this event for the given pointer index that
* occurred between this event and the previous motion event.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical Y coordinate of this event for the given pointer index that
* occurred between this event and the previous motion event.
* Whole numbers are pixels; the value may have a fraction for input devices
* that are sub-pixel precise. */
float motion_event_get_historical_y(input_event_t* motion_event, size_t pointer_index,
size_t history_index);
/* Get the historical pressure of this event for the given pointer index that
* occurred between this event and the previous motion event.
* The pressure generally ranges from 0 (no pressure at all) to 1 (normal pressure),
* however values higher than 1 may be generated depending on the calibration of
* the input device. */
float motion_event_get_historical_pressure(input_event_t* motion_event, size_t pointer_index,
size_t history_index);
/* Get the current scaled value of the approximate size for the given pointer index that
* occurred between this event and the previous motion event.
* This represents some approximation of the area of the screen being
* pressed; the actual value in pixels corresponding to the
* touch is normalized with the device specific range of values
* and scaled to a value between 0 and 1. The value of size can be used to
* determine fat touch events. */
float motion_event_get_historical_size(input_event_t* motion_event, size_t pointer_index,
size_t history_index);
#ifdef __cplusplus
}
#endif
#endif // _ANDROID_INPUT_H

View File

@@ -0,0 +1,158 @@
/*
* 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_KEYCODES_H
#define _ANDROID_KEYCODES_H
/******************************************************************
*
* IMPORTANT NOTICE:
*
* This file is part of Android's set of stable system headers
* exposed by the Android NDK (Native Development Kit).
*
* Third-party source AND binary code relies on the definitions
* here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
*
* - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
* - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
* - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Key codes.
*
* XXX: The declarations in <ui/KeycodeLabel.h> should be updated to use these instead.
* We should probably move this into android/keycodes.h and add some new API for
* getting labels so that we can remove the other tables also in KeycodeLabel.h.
*/
enum {
KEYCODE_UNKNOWN = 0,
KEYCODE_SOFT_LEFT = 1,
KEYCODE_SOFT_RIGHT = 2,
KEYCODE_HOME = 3,
KEYCODE_BACK = 4,
KEYCODE_CALL = 5,
KEYCODE_ENDCALL = 6,
KEYCODE_0 = 7,
KEYCODE_1 = 8,
KEYCODE_2 = 9,
KEYCODE_3 = 10,
KEYCODE_4 = 11,
KEYCODE_5 = 12,
KEYCODE_6 = 13,
KEYCODE_7 = 14,
KEYCODE_8 = 15,
KEYCODE_9 = 16,
KEYCODE_STAR = 17,
KEYCODE_POUND = 18,
KEYCODE_DPAD_UP = 19,
KEYCODE_DPAD_DOWN = 20,
KEYCODE_DPAD_LEFT = 21,
KEYCODE_DPAD_RIGHT = 22,
KEYCODE_DPAD_CENTER = 23,
KEYCODE_VOLUME_UP = 24,
KEYCODE_VOLUME_DOWN = 25,
KEYCODE_POWER = 26,
KEYCODE_CAMERA = 27,
KEYCODE_CLEAR = 28,
KEYCODE_A = 29,
KEYCODE_B = 30,
KEYCODE_C = 31,
KEYCODE_D = 32,
KEYCODE_E = 33,
KEYCODE_F = 34,
KEYCODE_G = 35,
KEYCODE_H = 36,
KEYCODE_I = 37,
KEYCODE_J = 38,
KEYCODE_K = 39,
KEYCODE_L = 40,
KEYCODE_M = 41,
KEYCODE_N = 42,
KEYCODE_O = 43,
KEYCODE_P = 44,
KEYCODE_Q = 45,
KEYCODE_R = 46,
KEYCODE_S = 47,
KEYCODE_T = 48,
KEYCODE_U = 49,
KEYCODE_V = 50,
KEYCODE_W = 51,
KEYCODE_X = 52,
KEYCODE_Y = 53,
KEYCODE_Z = 54,
KEYCODE_COMMA = 55,
KEYCODE_PERIOD = 56,
KEYCODE_ALT_LEFT = 57,
KEYCODE_ALT_RIGHT = 58,
KEYCODE_SHIFT_LEFT = 59,
KEYCODE_SHIFT_RIGHT = 60,
KEYCODE_TAB = 61,
KEYCODE_SPACE = 62,
KEYCODE_SYM = 63,
KEYCODE_EXPLORER = 64,
KEYCODE_ENVELOPE = 65,
KEYCODE_ENTER = 66,
KEYCODE_DEL = 67,
KEYCODE_GRAVE = 68,
KEYCODE_MINUS = 69,
KEYCODE_EQUALS = 70,
KEYCODE_LEFT_BRACKET = 71,
KEYCODE_RIGHT_BRACKET = 72,
KEYCODE_BACKSLASH = 73,
KEYCODE_SEMICOLON = 74,
KEYCODE_APOSTROPHE = 75,
KEYCODE_SLASH = 76,
KEYCODE_AT = 77,
KEYCODE_NUM = 78,
KEYCODE_HEADSETHOOK = 79,
KEYCODE_FOCUS = 80, // *Camera* focus
KEYCODE_PLUS = 81,
KEYCODE_MENU = 82,
KEYCODE_NOTIFICATION = 83,
KEYCODE_SEARCH = 84,
KEYCODE_MEDIA_PLAY_PAUSE= 85,
KEYCODE_MEDIA_STOP = 86,
KEYCODE_MEDIA_NEXT = 87,
KEYCODE_MEDIA_PREVIOUS = 88,
KEYCODE_MEDIA_REWIND = 89,
KEYCODE_MEDIA_FAST_FORWARD = 90,
KEYCODE_MUTE = 91,
KEYCODE_PAGE_UP = 92,
KEYCODE_PAGE_DOWN = 93
/* NOTE: If you add a new keycode here you must also add it to:
* native/include/android/keycodes.h
* frameworks/base/include/ui/KeycodeLabels.h
* frameworks/base/core/java/android/view/KeyEvent.java
* tools/puppet_master/PuppetMaster.nav_keys.py
* frameworks/base/core/res/res/values/attrs.xml
*/
};
#ifdef __cplusplus
}
#endif
#endif // _ANDROID_KEYCODES_H

View File

@@ -86,7 +86,7 @@ import android.view.WindowManagerPolicy;
* This class is created by the initialization routine of the {@link WindowManagerPolicy},
* and runs on its thread. The keyguard UI is created from that thread in the
* constructor of this class. The apis may be called from other threads, including the
* {@link com.android.server.KeyInputQueue}'s and {@link android.view.WindowManager}'s.
* {@link com.android.server.InputManager}'s and {@link android.view.WindowManager}'s.
* Therefore, methods on this class are synchronized, and any action that is pointed
* directly to the keyguard UI is posted to a {@link Handler} to ensure it is taken on the UI
* thread of the keyguard.

View File

@@ -1641,6 +1641,36 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
return false;
}
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
// lid changed state
mLidOpen = lidOpen;
boolean awakeNow = mKeyguardMediator.doLidChangeTq(mLidOpen);
updateRotation(Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE);
if (awakeNow) {
// If the lid opening and we don't have to keep the
// keyguard up, then we can turn on the screen
// immediately.
mKeyguardMediator.pokeWakelock();
} else if (keyguardIsShowingTq()) {
if (mLidOpen) {
// If we are opening the lid and not hiding the
// keyguard, then we need to have it turn on the
// screen once it is shown.
mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(
KeyEvent.KEYCODE_POWER);
}
} else {
// Light up the keyboard if we are sliding up.
if (mLidOpen) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
LocalPowerManager.BUTTON_EVENT);
} else {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false,
LocalPowerManager.OTHER_EVENT);
}
}
}
/** {@inheritDoc} */

View File

@@ -0,0 +1,460 @@
/*
* 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.server;
import com.android.internal.util.XmlUtils;
import com.android.server.KeyInputQueue.VirtualKey;
import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Environment;
import android.os.LocalPowerManager;
import android.os.PowerManager;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import android.view.InputChannel;
import android.view.InputTarget;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RawInputEvent;
import android.view.Surface;
import android.view.WindowManagerPolicy;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
/*
* Wraps the C++ InputManager and provides its callbacks.
*
* XXX Tempted to promote this to a first-class service, ie. InputManagerService, to
* improve separation of concerns with respect to the window manager.
*/
public class InputManager {
static final String TAG = "InputManager";
private final Callbacks mCallbacks;
private final Context mContext;
private final WindowManagerService mWindowManagerService;
private final WindowManagerPolicy mWindowManagerPolicy;
private final PowerManager mPowerManager;
private final PowerManagerService mPowerManagerService;
private int mTouchScreenConfig;
private int mKeyboardConfig;
private int mNavigationConfig;
private static native void nativeInit(Callbacks callbacks);
private static native void nativeStart();
private static native void nativeSetDisplaySize(int displayId, int width, int height);
private static native void nativeSetDisplayOrientation(int displayId, int rotation);
private static native int nativeGetScanCodeState(int deviceId, int deviceClasses,
int scanCode);
private static native int nativeGetKeyCodeState(int deviceId, int deviceClasses,
int keyCode);
private static native int nativeGetSwitchState(int deviceId, int deviceClasses,
int sw);
private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
private static native void nativeRegisterInputChannel(InputChannel inputChannel);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
// Device class as defined by EventHub.
private static final int CLASS_KEYBOARD = 0x00000001;
private static final int CLASS_ALPHAKEY = 0x00000002;
private static final int CLASS_TOUCHSCREEN = 0x00000004;
private static final int CLASS_TRACKBALL = 0x00000008;
private static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
private static final int CLASS_DPAD = 0x00000020;
public InputManager(Context context,
WindowManagerService windowManagerService,
WindowManagerPolicy windowManagerPolicy,
PowerManager powerManager,
PowerManagerService powerManagerService) {
this.mContext = context;
this.mWindowManagerService = windowManagerService;
this.mWindowManagerPolicy = windowManagerPolicy;
this.mPowerManager = powerManager;
this.mPowerManagerService = powerManagerService;
this.mCallbacks = new Callbacks();
mTouchScreenConfig = Configuration.TOUCHSCREEN_NOTOUCH;
mKeyboardConfig = Configuration.KEYBOARD_NOKEYS;
mNavigationConfig = Configuration.NAVIGATION_NONAV;
init();
}
private void init() {
Slog.i(TAG, "Initializing input manager");
nativeInit(mCallbacks);
}
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart();
}
public void setDisplaySize(int displayId, int width, int height) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Invalid display id or dimensions.");
}
Slog.i(TAG, "Setting display #" + displayId + " size to " + width + "x" + height);
nativeSetDisplaySize(displayId, width, height);
}
public void setDisplayOrientation(int displayId, int rotation) {
if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
throw new IllegalArgumentException("Invalid rotation.");
}
Slog.i(TAG, "Setting display #" + displayId + " orientation to " + rotation);
nativeSetDisplayOrientation(displayId, rotation);
}
public void getInputConfiguration(Configuration config) {
if (config == null) {
throw new IllegalArgumentException("config must not be null.");
}
config.touchscreen = mTouchScreenConfig;
config.keyboard = mKeyboardConfig;
config.navigation = mNavigationConfig;
}
public int getScancodeState(int code) {
return nativeGetScanCodeState(0, -1, code);
}
public int getScancodeState(int deviceId, int code) {
return nativeGetScanCodeState(deviceId, -1, code);
}
public int getTrackballScancodeState(int code) {
return nativeGetScanCodeState(-1, CLASS_TRACKBALL, code);
}
public int getDPadScancodeState(int code) {
return nativeGetScanCodeState(-1, CLASS_DPAD, code);
}
public int getKeycodeState(int code) {
return nativeGetKeyCodeState(0, -1, code);
}
public int getKeycodeState(int deviceId, int code) {
return nativeGetKeyCodeState(deviceId, -1, code);
}
public int getTrackballKeycodeState(int code) {
return nativeGetKeyCodeState(-1, CLASS_TRACKBALL, code);
}
public int getDPadKeycodeState(int code) {
return nativeGetKeyCodeState(-1, CLASS_DPAD, code);
}
public int getSwitchState(int sw) {
return nativeGetSwitchState(-1, -1, sw);
}
public int getSwitchState(int deviceId, int sw) {
return nativeGetSwitchState(deviceId, -1, sw);
}
public boolean hasKeys(int[] keyCodes, boolean[] keyExists) {
if (keyCodes == null) {
throw new IllegalArgumentException("keyCodes must not be null.");
}
if (keyExists == null) {
throw new IllegalArgumentException("keyExists must not be null.");
}
return nativeHasKeys(keyCodes, keyExists);
}
public void registerInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(inputChannel);
}
public void unregisterInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeUnregisterInputChannel(inputChannel);
}
// TBD where this really belongs, duplicate copy in WindowManagerService
static final int INJECT_FAILED = 0;
static final int INJECT_SUCCEEDED = 1;
static final int INJECT_NO_PERMISSION = -1;
/**
* Injects a key event into the event system on behalf of an application.
* @param event The event to inject.
* @param nature The nature of the event.
* @param sync If true, waits for the event to be completed before returning.
* @param pid The pid of the injecting application.
* @param uid The uid of the injecting application.
* @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
*/
public int injectKeyEvent(KeyEvent event, int nature, boolean sync, int pid, int uid) {
// TODO
return INJECT_FAILED;
}
/**
* Injects a motion event into the event system on behalf of an application.
* @param event The event to inject.
* @param nature The nature of the event.
* @param sync If true, waits for the event to be completed before returning.
* @param pid The pid of the injecting application.
* @param uid The uid of the injecting application.
* @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
*/
public int injectMotionEvent(MotionEvent event, int nature, boolean sync, int pid, int uid) {
// TODO
return INJECT_FAILED;
}
public void dump(PrintWriter pw) {
// TODO
}
private static final class VirtualKeyDefinition {
public int scanCode;
// configured position data, specified in display coords
public int centerX;
public int centerY;
public int width;
public int height;
}
/*
* Callbacks from native.
*/
private class Callbacks {
static final String TAG = "InputManager-Callbacks";
private static final boolean DEBUG_VIRTUAL_KEYS = false;
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
private final InputTargetList mReusableInputTargetList = new InputTargetList();
@SuppressWarnings("unused")
public boolean isScreenOn() {
return mPowerManagerService.isScreenOn();
}
@SuppressWarnings("unused")
public boolean isScreenBright() {
return mPowerManagerService.isScreenBright();
}
@SuppressWarnings("unused")
public void virtualKeyFeedback(long whenNanos, int deviceId, int action, int flags,
int keyCode, int scanCode, int metaState, long downTimeNanos) {
KeyEvent keyEvent = new KeyEvent(downTimeNanos / 1000000,
whenNanos / 1000000, action, keyCode, 0, metaState, scanCode, deviceId,
flags);
mWindowManagerService.virtualKeyFeedback(keyEvent);
}
@SuppressWarnings("unused")
public void notifyConfigurationChanged(long whenNanos,
int touchScreenConfig, int keyboardConfig, int navigationConfig) {
mTouchScreenConfig = touchScreenConfig;
mKeyboardConfig = keyboardConfig;
mNavigationConfig = navigationConfig;
mWindowManagerService.sendNewConfiguration();
}
@SuppressWarnings("unused")
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) {
mWindowManagerPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
}
@SuppressWarnings("unused")
public int hackInterceptKey(int deviceId, int type, int scanCode,
int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
RawInputEvent event = new RawInputEvent();
event.deviceId = deviceId;
event.type = type;
event.scancode = scanCode;
event.keycode = keyCode;
event.flags = policyFlags;
event.value = value;
event.when = whenNanos / 1000000;
return mWindowManagerPolicy.interceptKeyTq(event, isScreenOn);
}
@SuppressWarnings("unused")
public void goToSleep(long whenNanos) {
long when = whenNanos / 1000000;
mPowerManager.goToSleep(when);
}
@SuppressWarnings("unused")
public void pokeUserActivityForKey(long whenNanos) {
long when = whenNanos / 1000000;
mPowerManagerService.userActivity(when, false,
LocalPowerManager.BUTTON_EVENT, false);
}
@SuppressWarnings("unused")
public void notifyAppSwitchComing() {
mWindowManagerService.mKeyWaiter.appSwitchComing();
}
@SuppressWarnings("unused")
public boolean filterTouchEvents() {
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_filterTouchEvents);
}
@SuppressWarnings("unused")
public boolean filterJumpyTouchEvents() {
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_filterJumpyTouchEvents);
}
@SuppressWarnings("unused")
public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
try {
FileInputStream fis = new FileInputStream(
"/sys/board_properties/virtualkeys." + deviceName);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr, 2048);
String str = br.readLine();
if (str != null) {
String[] it = str.split(":");
if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
final int N = it.length-6;
for (int i=0; i<=N; i+=6) {
if (!"0x01".equals(it[i])) {
Slog.w(TAG, "Unknown virtual key type at elem #" + i
+ ": " + it[i]);
continue;
}
try {
VirtualKeyDefinition key = new VirtualKeyDefinition();
key.scanCode = Integer.parseInt(it[i+1]);
key.centerX = Integer.parseInt(it[i+2]);
key.centerY = Integer.parseInt(it[i+3]);
key.width = Integer.parseInt(it[i+4]);
key.height = Integer.parseInt(it[i+5]);
if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
+ key.scanCode + ": center=" + key.centerX + ","
+ key.centerY + " size=" + key.width + "x"
+ key.height);
keys.add(key);
} catch (NumberFormatException e) {
Slog.w(TAG, "Bad number at region " + i + " in: "
+ str, e);
}
}
}
br.close();
} catch (FileNotFoundException e) {
Slog.i(TAG, "No virtual keys found");
} catch (IOException e) {
Slog.w(TAG, "Error reading virtual keys", e);
}
return keys.toArray(new VirtualKeyDefinition[keys.size()]);
}
@SuppressWarnings("unused")
public String[] getExcludedDeviceNames() {
ArrayList<String> names = new ArrayList<String>();
// Read partner-provided list of excluded input devices
XmlPullParser parser = null;
// Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
FileReader confreader = null;
try {
confreader = new FileReader(confFile);
parser = Xml.newPullParser();
parser.setInput(confreader);
XmlUtils.beginDocument(parser, "devices");
while (true) {
XmlUtils.nextElement(parser);
if (!"device".equals(parser.getName())) {
break;
}
String name = parser.getAttributeValue(null, "name");
if (name != null) {
names.add(name);
}
}
} catch (FileNotFoundException e) {
// It's ok if the file does not exist.
} catch (Exception e) {
Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
} finally {
try { if (confreader != null) confreader.close(); } catch (IOException e) { }
}
return names.toArray(new String[names.size()]);
}
@SuppressWarnings("unused")
public InputTarget[] getKeyEventTargets(KeyEvent event, int nature, int policyFlags) {
mReusableInputTargetList.clear();
mWindowManagerService.getKeyEventTargets(mReusableInputTargetList,
event, nature, policyFlags);
return mReusableInputTargetList.toNullTerminatedArray();
}
@SuppressWarnings("unused")
public InputTarget[] getMotionEventTargets(MotionEvent event, int nature, int policyFlags) {
mReusableInputTargetList.clear();
mWindowManagerService.getMotionEventTargets(mReusableInputTargetList,
event, nature, policyFlags);
return mReusableInputTargetList.toNullTerminatedArray();
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.server;
import android.view.InputChannel;
import android.view.InputTarget;
/**
* A specialized list of input targets backed by an array.
*
* This class is part of an InputManager optimization to avoid allocating and copying
* input target arrays unnecessarily on return from JNI callbacks. Internally, it keeps
* an array full of demand-allocated InputTarget objects that it recycles each time the
* list is cleared. The used portion of the array is padded with a null.
*
* @hide
*/
public class InputTargetList {
private InputTarget[] mArray;
private int mCount;
/**
* Creates an empty input target list.
*/
public InputTargetList() {
mArray = new InputTarget[8];
}
/**
* Clears the input target list.
*/
public void clear() {
if (mCount == 0) {
return;
}
int count = mCount;
mCount = 0;
mArray[count] = mArray[0];
while (count > 0) {
count -= 1;
mArray[count].recycle();
}
// mArray[0] could be set to null here but we do it in toNullTerminatedArray()
}
/**
* Adds a new input target to the input target list.
* @param inputChannel The input channel of the target window.
* @param flags Input target flags.
* @param timeoutNanos The input dispatch timeout (before ANR) in nanoseconds or -1 if none.
* @param xOffset An offset to add to motion X coordinates during delivery.
* @param yOffset An offset to add to motion Y coordinates during delivery.
*/
public void add(InputChannel inputChannel, int flags, long timeoutNanos,
float xOffset, float yOffset) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (mCount + 1 == mArray.length) {
InputTarget[] oldArray = mArray;
mArray = new InputTarget[oldArray.length * 2];
System.arraycopy(oldArray, 0, mArray, 0, mCount);
}
// Grab InputTarget from tail (after used section) if available.
InputTarget inputTarget = mArray[mCount + 1];
if (inputTarget == null) {
inputTarget = new InputTarget();
}
inputTarget.mInputChannel = inputChannel;
inputTarget.mFlags = flags;
inputTarget.mTimeoutNanos = timeoutNanos;
inputTarget.mXOffset = xOffset;
inputTarget.mYOffset = yOffset;
mArray[mCount] = inputTarget;
mCount += 1;
// mArray[mCount] could be set to null here but we do it in toNullTerminatedArray()
}
/**
* Gets the input targets as a null-terminated array.
* @return The input target array.
*/
public InputTarget[] toNullTerminatedArray() {
mArray[mCount] = null;
return mArray;
}
}

View File

@@ -298,7 +298,9 @@ public abstract class KeyInputQueue {
mHapticFeedbackCallback = hapticFeedbackCallback;
readExcludedDevices();
if (! WindowManagerService.ENABLE_NATIVE_INPUT_DISPATCH) {
readExcludedDevices();
}
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
@@ -311,7 +313,9 @@ public abstract class KeyInputQueue {
mFirst.next = mLast;
mLast.prev = mFirst;
mThread.start();
if (! WindowManagerService.ENABLE_NATIVE_INPUT_DISPATCH) {
mThread.start();
}
}
public void setDisplay(Display display) {

View File

@@ -101,6 +101,9 @@ import android.view.IRotationWatcher;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputQueue;
import android.view.InputTarget;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RawInputEvent;
@@ -157,6 +160,8 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean MEASURE_LATENCY = false;
static final boolean ENABLE_NATIVE_INPUT_DISPATCH =
WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH;
static private LatencyTimer lt;
static final boolean PROFILE_ORIENTATION = false;
@@ -497,10 +502,12 @@ public class WindowManagerService extends IWindowManager.Stub
final KeyWaiter mKeyWaiter = new KeyWaiter();
final KeyQ mQueue;
final InputManager mInputManager;
final InputDispatcherThread mInputThread;
// Who is holding the screen on.
Session mHoldingScreenOn;
PowerManager.WakeLock mHoldingScreenWakeLock;
boolean mTurnOnScreen;
@@ -650,8 +657,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
mMinWaitTimeBetweenTouchEvents = 1000 / max_events_per_sec;
mQueue = new KeyQ();
mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"KEEP_SCREEN_ON_FLAG");
mHoldingScreenWakeLock.setReferenceCounted(false);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
mInputManager = new InputManager(context, this, mPolicy, pmc, mPowerManager);
} else {
mInputManager = null;
}
mQueue = new KeyQ();
mInputThread = new InputDispatcherThread();
PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
@@ -666,7 +681,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
mInputThread.start();
if (ENABLE_NATIVE_INPUT_DISPATCH) {
mInputManager.start();
} else {
mInputThread.start();
}
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
@@ -1859,7 +1878,7 @@ public class WindowManagerService extends IWindowManager.Stub
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
Rect outContentInsets, InputChannel outInputChannel) {
int res = mPolicy.checkAddPermission(attrs);
if (res != WindowManagerImpl.ADD_OKAY) {
return res;
@@ -1878,7 +1897,12 @@ public class WindowManagerService extends IWindowManager.Stub
mDisplay = wm.getDefaultDisplay();
mInitialDisplayWidth = mDisplay.getWidth();
mInitialDisplayHeight = mDisplay.getHeight();
mQueue.setDisplay(mDisplay);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
mInputManager.setDisplaySize(0,
mInitialDisplayWidth, mInitialDisplayHeight);
} else {
mQueue.setDisplay(mDisplay);
}
reportNewConfig = true;
}
@@ -1971,6 +1995,17 @@ public class WindowManagerService extends IWindowManager.Stub
if (res != WindowManagerImpl.ADD_OKAY) {
return res;
}
if (ENABLE_NATIVE_INPUT_DISPATCH) {
if (outInputChannel != null) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.mInputChannel = inputChannels[0];
inputChannels[1].transferToBinderOutParameter(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel);
}
}
// From now on, no exceptions or errors allowed!
@@ -4354,7 +4389,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getSwitchState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return KeyInputQueue.getSwitchState(sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getSwitchState(sw);
} else {
return KeyInputQueue.getSwitchState(sw);
}
}
public int getSwitchStateForDevice(int devid, int sw) {
@@ -4362,7 +4401,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getSwitchStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return KeyInputQueue.getSwitchState(devid, sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getSwitchState(devid, sw);
} else {
return KeyInputQueue.getSwitchState(devid, sw);
}
}
public int getScancodeState(int sw) {
@@ -4370,7 +4413,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return mQueue.getScancodeState(sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getScancodeState(sw);
} else {
return mQueue.getScancodeState(sw);
}
}
public int getScancodeStateForDevice(int devid, int sw) {
@@ -4378,7 +4425,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getScancodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return mQueue.getScancodeState(devid, sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getScancodeState(devid, sw);
} else {
return mQueue.getScancodeState(devid, sw);
}
}
public int getTrackballScancodeState(int sw) {
@@ -4386,7 +4437,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getTrackballScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return mQueue.getTrackballScancodeState(sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getTrackballScancodeState(sw);
} else {
return mQueue.getTrackballScancodeState(sw);
}
}
public int getDPadScancodeState(int sw) {
@@ -4394,7 +4449,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getDPadScancodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return mQueue.getDPadScancodeState(sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getDPadScancodeState(sw);
} else {
return mQueue.getDPadScancodeState(sw);
}
}
public int getKeycodeState(int sw) {
@@ -4402,7 +4461,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return mQueue.getKeycodeState(sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getKeycodeState(sw);
} else {
return mQueue.getKeycodeState(sw);
}
}
public int getKeycodeStateForDevice(int devid, int sw) {
@@ -4410,7 +4473,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getKeycodeStateForDevice()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return mQueue.getKeycodeState(devid, sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getKeycodeState(devid, sw);
} else {
return mQueue.getKeycodeState(devid, sw);
}
}
public int getTrackballKeycodeState(int sw) {
@@ -4418,7 +4485,11 @@ public class WindowManagerService extends IWindowManager.Stub
"getTrackballKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return mQueue.getTrackballKeycodeState(sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getTrackballKeycodeState(sw);
} else {
return mQueue.getTrackballKeycodeState(sw);
}
}
public int getDPadKeycodeState(int sw) {
@@ -4426,11 +4497,19 @@ public class WindowManagerService extends IWindowManager.Stub
"getDPadKeycodeState()")) {
throw new SecurityException("Requires READ_INPUT_STATE permission");
}
return mQueue.getDPadKeycodeState(sw);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.getDPadKeycodeState(sw);
} else {
return mQueue.getDPadKeycodeState(sw);
}
}
public boolean hasKeys(int[] keycodes, boolean[] keyExists) {
return KeyInputQueue.hasKeys(keycodes, keyExists);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
return mInputManager.hasKeys(keycodes, keyExists);
} else {
return KeyInputQueue.hasKeys(keycodes, keyExists);
}
}
public void enableScreenAfterBoot() {
@@ -4575,7 +4654,11 @@ public class WindowManagerService extends IWindowManager.Stub
mLayoutNeeded = true;
startFreezingDisplayLocked();
Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags);
mQueue.setOrientation(rotation);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
mInputManager.setDisplayOrientation(0, rotation);
} else {
mQueue.setOrientation(rotation);
}
if (mDisplayEnabled) {
Surface.setOrientation(0, rotation, animFlags);
}
@@ -4906,7 +4989,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (mDisplay == null) {
return false;
}
mQueue.getInputConfiguration(config);
if (ENABLE_NATIVE_INPUT_DISPATCH) {
mInputManager.getInputConfiguration(config);
} else {
mQueue.getInputConfiguration(config);
}
// Use the effective "visual" dimensions based on current rotation
final boolean rotated = (mRotation == Surface.ROTATION_90
@@ -4989,6 +5076,291 @@ public class WindowManagerService extends IWindowManager.Stub
// -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
public void getKeyEventTargets(InputTargetList inputTargets,
KeyEvent event, int nature, int policyFlags) {
if (DEBUG_INPUT) Slog.v(TAG, "Dispatch key: " + event);
// TODO what do we do with mDisplayFrozen?
// TODO what do we do with focus.mToken.paused?
WindowState focus = getFocusedWindow();
wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
}
// Target of Motion events
WindowState mTouchFocus;
// Windows above the target who would like to receive an "outside"
// touch event for any down events outside of them.
// (This is a linked list by way of WindowState.mNextOutsideTouch.)
WindowState mOutsideTouchTargets;
private void clearTouchFocus() {
mTouchFocus = null;
mOutsideTouchTargets = null;
}
public void getMotionEventTargets(InputTargetList inputTargets,
MotionEvent event, int nature, int policyFlags) {
if (nature == InputQueue.INPUT_EVENT_NATURE_TRACKBALL) {
// More or less the same as for keys...
WindowState focus = getFocusedWindow();
wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
return;
}
int action = event.getAction();
// TODO detect cheek presses somewhere... either here or in native code
final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
WindowState target = mTouchFocus;
if (action == MotionEvent.ACTION_UP) {
// let go of our target
mPowerManager.logPointerUpEvent();
clearTouchFocus();
} else if (action == MotionEvent.ACTION_DOWN) {
// acquire a new target
mPowerManager.logPointerDownEvent();
synchronized (mWindowMap) {
if (mTouchFocus != null) {
// this is weird, we got a pen down, but we thought it was
// already down!
// XXX: We should probably send an ACTION_UP to the current
// target.
Slog.w(TAG, "Pointer down received while already down in: "
+ mTouchFocus);
clearTouchFocus();
}
// ACTION_DOWN is special, because we need to lock next events to
// the window we'll land onto.
final int x = (int) event.getX();
final int y = (int) event.getY();
final ArrayList windows = mWindows;
final int N = windows.size();
WindowState topErrWindow = null;
final Rect tmpRect = mTempRect;
for (int i=N-1; i>=0; i--) {
WindowState child = (WindowState)windows.get(i);
//Slog.i(TAG, "Checking dispatch to: " + child);
final int flags = child.mAttrs.flags;
if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
if (topErrWindow == null) {
topErrWindow = child;
}
}
if (!child.isVisibleLw()) {
//Slog.i(TAG, "Not visible!");
continue;
}
if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
//Slog.i(TAG, "Not touchable!");
if ((flags & WindowManager.LayoutParams
.FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
child.mNextOutsideTouch = mOutsideTouchTargets;
mOutsideTouchTargets = child;
}
continue;
}
tmpRect.set(child.mFrame);
if (child.mTouchableInsets == ViewTreeObserver
.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
// The touch is inside of the window if it is
// inside the frame, AND the content part of that
// frame that was given by the application.
tmpRect.left += child.mGivenContentInsets.left;
tmpRect.top += child.mGivenContentInsets.top;
tmpRect.right -= child.mGivenContentInsets.right;
tmpRect.bottom -= child.mGivenContentInsets.bottom;
} else if (child.mTouchableInsets == ViewTreeObserver
.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE) {
// The touch is inside of the window if it is
// inside the frame, AND the visible part of that
// frame that was given by the application.
tmpRect.left += child.mGivenVisibleInsets.left;
tmpRect.top += child.mGivenVisibleInsets.top;
tmpRect.right -= child.mGivenVisibleInsets.right;
tmpRect.bottom -= child.mGivenVisibleInsets.bottom;
}
final int touchFlags = flags &
(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
if (tmpRect.contains(x, y) || touchFlags == 0) {
//Slog.i(TAG, "Using this target!");
if (!screenWasOff || (flags &
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
mTouchFocus = child;
} else {
//Slog.i(TAG, "Waking, skip!");
mTouchFocus = null;
}
break;
}
if ((flags & WindowManager.LayoutParams
.FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
child.mNextOutsideTouch = mOutsideTouchTargets;
mOutsideTouchTargets = child;
//Slog.i(TAG, "Adding to outside target list: " + child);
}
}
// if there's an error window but it's not accepting
// focus (typically because it is not yet visible) just
// wait for it -- any other focused window may in fact
// be in ANR state.
if (topErrWindow != null && mTouchFocus != topErrWindow) {
mTouchFocus = null;
}
}
target = mTouchFocus;
}
if (target != null) {
wakeupIfNeeded(target, eventType(event));
}
int targetFlags = 0;
if (target == null) {
// In this case we are either dropping the event, or have received
// a move or up without a down. It is common to receive move
// events in such a way, since this means the user is moving the
// pointer without actually pressing down. All other cases should
// be atypical, so let's log them.
if (action != MotionEvent.ACTION_MOVE) {
Slog.w(TAG, "No window to dispatch pointer action " + action);
}
} else {
if ((target.mAttrs.flags &
WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
//target wants to ignore fat touch events
boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
//explicit flag to return without processing event further
boolean returnFlag = false;
if((action == MotionEvent.ACTION_DOWN)) {
mFatTouch = false;
if(cheekPress) {
mFatTouch = true;
returnFlag = true;
}
} else {
if(action == MotionEvent.ACTION_UP) {
if(mFatTouch) {
//earlier even was invalid doesnt matter if current up is cheekpress or not
mFatTouch = false;
returnFlag = true;
} else if(cheekPress) {
//cancel the earlier event
targetFlags |= InputTarget.FLAG_CANCEL;
action = MotionEvent.ACTION_CANCEL;
}
} else if(action == MotionEvent.ACTION_MOVE) {
if(mFatTouch) {
//two cases here
//an invalid down followed by 0 or moves(valid or invalid)
//a valid down, invalid move, more moves. want to ignore till up
returnFlag = true;
} else if(cheekPress) {
//valid down followed by invalid moves
//an invalid move have to cancel earlier action
targetFlags |= InputTarget.FLAG_CANCEL;
action = MotionEvent.ACTION_CANCEL;
if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE");
//note that the subsequent invalid moves will not get here
mFatTouch = true;
}
}
} //else if action
if(returnFlag) {
return;
}
} //end if target
}
synchronized (mWindowMap) {
if (target != null && ! target.isVisibleLw()) {
target = null;
}
if (action == MotionEvent.ACTION_DOWN) {
while (mOutsideTouchTargets != null) {
addInputTarget(inputTargets, mOutsideTouchTargets,
InputTarget.FLAG_OUTSIDE | targetFlags);
mOutsideTouchTargets = mOutsideTouchTargets.mNextOutsideTouch;
}
}
// If we sent an initial down to the wallpaper, then continue
// sending events until the final up.
// Alternately if we are on top of the wallpaper, then the wallpaper also
// gets to see this movement.
if (mSendingPointersToWallpaper ||
(target != null && action == MotionEvent.ACTION_DOWN
&& mWallpaperTarget == target
&& target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)) {
int curTokenIndex = mWallpaperTokens.size();
while (curTokenIndex > 0) {
curTokenIndex--;
WindowToken token = mWallpaperTokens.get(curTokenIndex);
int curWallpaperIndex = token.windows.size();
while (curWallpaperIndex > 0) {
curWallpaperIndex--;
WindowState wallpaper = token.windows.get(curWallpaperIndex);
if ((wallpaper.mAttrs.flags &
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
continue;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
mSendingPointersToWallpaper = true;
break;
case MotionEvent.ACTION_UP:
mSendingPointersToWallpaper = false;
break;
}
addInputTarget(inputTargets, wallpaper, targetFlags);
}
}
}
if (target != null) {
addInputTarget(inputTargets, target, InputTarget.FLAG_SYNC | targetFlags);
}
}
}
private void addInputTarget(InputTargetList inputTargets, WindowState window, int flags) {
if (window.mInputChannel == null) {
return;
}
long timeoutNanos = -1;
IApplicationToken appToken = window.getAppToken();
if (appToken != null) {
try {
timeoutNanos = appToken.getKeyDispatchingTimeout() * 1000000;
} catch (RemoteException ex) {
Slog.w(TAG, "Could not get key dispatching timeout.", ex);
}
}
inputTargets.add(window.mInputChannel, flags, timeoutNanos,
- window.mFrame.left, - window.mFrame.top);
}
private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
long curTime = SystemClock.uptimeMillis();
@@ -5499,10 +5871,18 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
final int result = dispatchKey(newEvent, pid, uid);
if (sync) {
mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
final int result;
if (ENABLE_NATIVE_INPUT_DISPATCH) {
result = mInputManager.injectKeyEvent(newEvent,
InputQueue.INPUT_EVENT_NATURE_KEY, sync, pid, uid);
} else {
result = dispatchKey(newEvent, pid, uid);
if (sync) {
mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
}
Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
@@ -5527,10 +5907,18 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
final int result = dispatchPointer(null, ev, pid, uid);
if (sync) {
mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
final int result;
if (ENABLE_NATIVE_INPUT_DISPATCH) {
result = mInputManager.injectMotionEvent(ev,
InputQueue.INPUT_EVENT_NATURE_TOUCH, sync, pid, uid);
} else {
result = dispatchPointer(null, ev, pid, uid);
if (sync) {
mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
}
Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
@@ -5555,10 +5943,18 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
final int result = dispatchTrackball(null, ev, pid, uid);
if (sync) {
mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
final int result;
if (ENABLE_NATIVE_INPUT_DISPATCH) {
result = mInputManager.injectMotionEvent(ev,
InputQueue.INPUT_EVENT_NATURE_TRACKBALL, sync, pid, uid);
} else {
result = dispatchTrackball(null, ev, pid, uid);
if (sync) {
mKeyWaiter.waitForNextEventTarget(null, null, null, false, true, pid, uid);
}
}
Binder.restoreCallingIdentity(ident);
switch (result) {
case INJECT_NO_PERMISSION:
@@ -6326,14 +6722,8 @@ public class WindowManagerService extends IWindowManager.Stub
private class KeyQ extends KeyInputQueue
implements KeyInputQueue.FilterCallback {
PowerManager.WakeLock mHoldingScreen;
KeyQ() {
super(mContext, WindowManagerService.this);
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"KEEP_SCREEN_ON_FLAG");
mHoldingScreen.setReferenceCounted(false);
}
@Override
@@ -6445,21 +6835,6 @@ public class WindowManagerService extends IWindowManager.Stub
return FILTER_KEEP;
}
}
/**
* Must be called with the main window manager lock held.
*/
void setHoldScreenLocked(boolean holding) {
boolean state = mHoldingScreen.isHeld();
if (holding != state) {
if (holding) {
mHoldingScreen.acquire();
} else {
mPolicy.screenOnStoppedLw();
mHoldingScreen.release();
}
}
}
}
public boolean detectSafeMode() {
@@ -6788,8 +7163,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
public int add(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets, InputChannel outInputChannel) {
return addWindow(this, window, attrs, viewVisibility, outContentInsets,
outInputChannel);
}
public int addWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
return addWindow(this, window, attrs, viewVisibility, outContentInsets);
return addWindow(this, window, attrs, viewVisibility, outContentInsets, null);
}
public void remove(IWindow window) {
@@ -7158,6 +7539,9 @@ public class WindowManagerService extends IWindowManager.Stub
int mSurfaceLayer;
float mSurfaceAlpha;
// Input channel
InputChannel mInputChannel;
WindowState(Session s, IWindow c, WindowToken token,
WindowState attachedWindow, WindowManager.LayoutParams a,
int viewVisibility) {
@@ -8182,6 +8566,15 @@ public class WindowManagerService extends IWindowManager.Stub
// Ignore if it has already been removed (usually because
// we are doing this as part of processing a death note.)
}
if (ENABLE_NATIVE_INPUT_DISPATCH) {
if (mInputChannel != null) {
mInputManager.unregisterInputChannel(mInputChannel);
mInputChannel.dispose();
mInputChannel = null;
}
}
}
private class DeathRecipient implements IBinder.DeathRecipient {
@@ -8424,6 +8817,11 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
}
}
String makeInputChannelName() {
return Integer.toHexString(System.identityHashCode(this))
+ " " + mAttrs.getTitle();
}
@Override
public String toString() {
@@ -9275,7 +9673,8 @@ public class WindowManagerService extends IWindowManager.Stub
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
return new Session(client, inputContext);
Session session = new Session(client, inputContext);
return session;
}
public boolean inputMethodClientHasFocus(IInputMethodClient client) {
@@ -10773,7 +11172,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FREEZE) Slog.v(TAG, "Layout: mDisplayFrozen=" + mDisplayFrozen
+ " holdScreen=" + holdScreen);
if (!mDisplayFrozen) {
mQueue.setHoldScreenLocked(holdScreen != null);
setHoldScreenLocked(holdScreen != null);
if (screenBrightness < 0 || screenBrightness > 1.0f) {
mPowerManager.setScreenBrightnessOverride(-1);
} else {
@@ -10804,6 +11203,21 @@ public class WindowManagerService extends IWindowManager.Stub
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
}
/**
* Must be called with the main window manager lock held.
*/
void setHoldScreenLocked(boolean holding) {
boolean state = mHoldingScreenWakeLock.isHeld();
if (holding != state) {
if (holding) {
mHoldingScreenWakeLock.acquire();
} else {
mPolicy.screenOnStoppedLw();
mHoldingScreenWakeLock.release();
}
}
}
void requestAnimationLocked(long delay) {
if (!mAnimationPending) {
@@ -11138,8 +11552,13 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
pw.println("Input State:");
mQueue.dump(pw, " ");
if (ENABLE_NATIVE_INPUT_DISPATCH) {
pw.println("Input Dispatcher State:");
mInputManager.dump(pw);
} else {
pw.println("Input State:");
mQueue.dump(pw, " ");
}
pw.println(" ");
synchronized(mWindowMap) {

View File

@@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
com_android_server_BatteryService.cpp \
com_android_server_KeyInputQueue.cpp \
com_android_server_InputManager.cpp \
com_android_server_LightsService.cpp \
com_android_server_SensorService.cpp \
com_android_server_SystemServer.cpp \
@@ -16,6 +17,7 @@ LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
libcutils \
libhardware \
libhardware_legacy \

View File

@@ -0,0 +1,746 @@
/*
* 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 "InputManager-JNI"
#include "JNIHelp.h"
#include "jni.h"
#include <android_runtime/AndroidRuntime.h>
#include <ui/InputManager.h>
#include <ui/InputTransport.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include "../../core/jni/android_view_KeyEvent.h"
#include "../../core/jni/android_view_MotionEvent.h"
#include "../../core/jni/android_view_InputChannel.h"
#include "../../core/jni/android_view_InputTarget.h"
namespace android {
class InputDispatchPolicy : public InputDispatchPolicyInterface {
public:
InputDispatchPolicy(JNIEnv* env, jobject callbacks);
virtual ~InputDispatchPolicy();
void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
void setDisplayOrientation(int32_t displayId, int32_t orientation);
virtual bool getDisplayInfo(int32_t displayId,
int32_t* width, int32_t* height, int32_t* orientation);
virtual void notifyConfigurationChanged(nsecs_t when,
int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig);
virtual void notifyLidSwitchChanged(nsecs_t when, bool lidOpen);
virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime);
virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
bool rolled);
virtual int32_t interceptTouch(nsecs_t when);
virtual bool filterTouchEvents();
virtual bool filterJumpyTouchEvents();
virtual void getVirtualKeyDefinitions(const String8& deviceName,
Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions);
virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
virtual bool allowKeyRepeat();
virtual nsecs_t getKeyRepeatTimeout();
virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
Vector<InputTarget>& outTargets);
virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
Vector<InputTarget>& outTargets);
private:
bool isScreenOn();
bool isScreenBright();
private:
jobject mCallbacks;
int32_t mFilterTouchEvents;
int32_t mFilterJumpyTouchEvents;
Mutex mDisplayLock;
int32_t mDisplayWidth, mDisplayHeight;
int32_t mDisplayOrientation;
inline JNIEnv* threadEnv() const {
return AndroidRuntime::getJNIEnv();
}
};
// globals
static sp<EventHub> gEventHub;
static sp<InputDispatchPolicy> gInputDispatchPolicy;
static sp<InputManager> gInputManager;
// JNI
static struct {
jclass clazz;
jmethodID isScreenOn;
jmethodID isScreenBright;
jmethodID notifyConfigurationChanged;
jmethodID notifyLidSwitchChanged;
jmethodID virtualKeyFeedback;
jmethodID hackInterceptKey;
jmethodID goToSleep;
jmethodID pokeUserActivityForKey;
jmethodID notifyAppSwitchComing;
jmethodID filterTouchEvents;
jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyDefinitions;
jmethodID getExcludedDeviceNames;
jmethodID getKeyEventTargets;
jmethodID getMotionEventTargets;
} gCallbacksClassInfo;
static struct {
jclass clazz;
jfieldID scanCode;
jfieldID centerX;
jfieldID centerY;
jfieldID width;
jfieldID height;
} gVirtualKeyDefinitionClassInfo;
static bool checkInputManagerUnitialized(JNIEnv* env) {
if (gInputManager == NULL) {
LOGE("Input manager not initialized.");
jniThrowRuntimeException(env, "Input manager not initialized.");
return true;
}
return false;
}
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
jobject callbacks) {
if (gEventHub == NULL) {
gEventHub = new EventHub();
}
if (gInputDispatchPolicy == NULL) {
gInputDispatchPolicy = new InputDispatchPolicy(env, callbacks);
}
if (gInputManager == NULL) {
gInputManager = new InputManager(gEventHub, gInputDispatchPolicy);
}
}
static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {
if (checkInputManagerUnitialized(env)) {
return;
}
status_t result = gInputManager->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
static void android_server_InputManager_nativeSetDisplaySize(JNIEnv* env, jclass clazz,
jint displayId, jint width, jint height) {
if (checkInputManagerUnitialized(env)) {
return;
}
// XXX we could get this from the SurfaceFlinger directly instead of requiring it
// to be passed in like this, not sure which is better but leaving it like this
// keeps the window manager in direct control of when display transitions propagate down
// to the input dispatcher
gInputDispatchPolicy->setDisplaySize(displayId, width, height);
}
static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, jclass clazz,
jint displayId, jint orientation) {
if (checkInputManagerUnitialized(env)) {
return;
}
gInputDispatchPolicy->setDisplayOrientation(displayId, orientation);
}
static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
jint deviceId, jint deviceClasses, jint scanCode) {
if (checkInputManagerUnitialized(env)) {
return KEY_STATE_UNKNOWN;
}
return gInputManager->getScanCodeState(deviceId, deviceClasses, scanCode);
}
static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
jint deviceId, jint deviceClasses, jint keyCode) {
if (checkInputManagerUnitialized(env)) {
return KEY_STATE_UNKNOWN;
}
return gInputManager->getKeyCodeState(deviceId, deviceClasses, keyCode);
}
static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz,
jint deviceId, jint deviceClasses, jint sw) {
if (checkInputManagerUnitialized(env)) {
return KEY_STATE_UNKNOWN;
}
return gInputManager->getSwitchState(deviceId, deviceClasses, sw);
}
static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz,
jintArray keyCodes, jbooleanArray outFlags) {
if (checkInputManagerUnitialized(env)) {
return JNI_FALSE;
}
int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
jsize numCodes = env->GetArrayLength(keyCodes);
jboolean result;
if (numCodes == env->GetArrayLength(outFlags)) {
result = gInputManager->hasKeys(numCodes, codes, flags);
} else {
result = JNI_FALSE;
}
env->ReleaseBooleanArrayElements(outFlags, flags, 0);
env->ReleaseIntArrayElements(keyCodes, codes, 0);
return result;
}
static void throwInputChannelNotInitialized(JNIEnv* env) {
jniThrowException(env, "java/lang/IllegalStateException",
"inputChannel is not initialized");
}
static void android_server_InputManager_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 manager!", inputChannel->getName().string());
gInputManager->unregisterInputChannel(inputChannel);
}
static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
jobject inputChannelObj) {
if (checkInputManagerUnitialized(env)) {
return;
}
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
status_t status = gInputManager->registerInputChannel(inputChannel);
if (status) {
jniThrowRuntimeException(env, "Failed to register input channel. "
"Check logs for details.");
return;
}
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
android_server_InputManager_handleInputChannelDisposed, NULL);
}
static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
jobject inputChannelObj) {
if (checkInputManagerUnitialized(env)) {
return;
}
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
status_t status = gInputManager->unregisterInputChannel(inputChannel);
if (status) {
jniThrowRuntimeException(env, "Failed to unregister input channel. "
"Check logs for details.");
}
}
static JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V",
(void*) android_server_InputManager_nativeInit },
{ "nativeStart", "()V",
(void*) android_server_InputManager_nativeStart },
{ "nativeSetDisplaySize", "(III)V",
(void*) android_server_InputManager_nativeSetDisplaySize },
{ "nativeSetDisplayOrientation", "(II)V",
(void*) android_server_InputManager_nativeSetDisplayOrientation },
{ "nativeGetScanCodeState", "(III)I",
(void*) android_server_InputManager_nativeGetScanCodeState },
{ "nativeGetKeyCodeState", "(III)I",
(void*) android_server_InputManager_nativeGetKeyCodeState },
{ "nativeGetSwitchState", "(III)I",
(void*) android_server_InputManager_nativeGetSwitchState },
{ "nativeHasKeys", "([I[Z)Z",
(void*) android_server_InputManager_nativeHasKeys },
{ "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeUnregisterInputChannel }
};
#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_server_InputManager(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "com/android/server/InputManager",
gInputManagerMethods, NELEM(gInputManagerMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
// Policy
FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks");
GET_METHOD_ID(gCallbacksClassInfo.isScreenOn, gCallbacksClassInfo.clazz,
"isScreenOn", "()Z");
GET_METHOD_ID(gCallbacksClassInfo.isScreenBright, gCallbacksClassInfo.clazz,
"isScreenBright", "()Z");
GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz,
"notifyConfigurationChanged", "(JIII)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz,
"notifyLidSwitchChanged", "(JZ)V");
GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz,
"virtualKeyFeedback", "(JIIIIIIJ)V");
GET_METHOD_ID(gCallbacksClassInfo.hackInterceptKey, gCallbacksClassInfo.clazz,
"hackInterceptKey", "(IIIIIIJZ)I");
GET_METHOD_ID(gCallbacksClassInfo.goToSleep, gCallbacksClassInfo.clazz,
"goToSleep", "(J)V");
GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivityForKey, gCallbacksClassInfo.clazz,
"pokeUserActivityForKey", "(J)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
"notifyAppSwitchComing", "()V");
GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz,
"filterTouchEvents", "()Z");
GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz,
"filterJumpyTouchEvents", "()Z");
GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyDefinitions, gCallbacksClassInfo.clazz,
"getVirtualKeyDefinitions",
"(Ljava/lang/String;)[Lcom/android/server/InputManager$VirtualKeyDefinition;");
GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz,
"getKeyEventTargets", "(Landroid/view/KeyEvent;II)[Landroid/view/InputTarget;");
GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz,
"getMotionEventTargets", "(Landroid/view/MotionEvent;II)[Landroid/view/InputTarget;");
// VirtualKeyDefinition
FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
"com/android/server/InputManager$VirtualKeyDefinition");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.scanCode, gVirtualKeyDefinitionClassInfo.clazz,
"scanCode", "I");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerX, gVirtualKeyDefinitionClassInfo.clazz,
"centerX", "I");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerY, gVirtualKeyDefinitionClassInfo.clazz,
"centerY", "I");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.width, gVirtualKeyDefinitionClassInfo.clazz,
"width", "I");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
"height", "I");
return 0;
}
// static functions
static bool isAppSwitchKey(int32_t keyCode) {
return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
}
static bool checkException(JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) {
LOGE("An exception was thrown by an InputDispatchPolicy callback '%s'.", methodName);
LOGE_EX(env);
env->ExceptionClear();
return true;
}
return false;
}
// InputDispatchPolicy implementation
InputDispatchPolicy::InputDispatchPolicy(JNIEnv* env, jobject callbacks) :
mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
mCallbacks = env->NewGlobalRef(callbacks);
}
InputDispatchPolicy::~InputDispatchPolicy() {
JNIEnv* env = threadEnv();
env->DeleteGlobalRef(mCallbacks);
}
void InputDispatchPolicy::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
if (displayId == 0) {
AutoMutex _l(mDisplayLock);
mDisplayWidth = width;
mDisplayHeight = height;
}
}
void InputDispatchPolicy::setDisplayOrientation(int32_t displayId, int32_t orientation) {
if (displayId == 0) {
AutoMutex _l(mDisplayLock);
mDisplayOrientation = orientation;
}
}
bool InputDispatchPolicy::getDisplayInfo(int32_t displayId,
int32_t* width, int32_t* height, int32_t* orientation) {
bool result = false;
if (displayId == 0) {
AutoMutex _l(mDisplayLock);
if (mDisplayWidth > 0) {
*width = mDisplayWidth;
*height = mDisplayHeight;
*orientation = mDisplayOrientation;
result = true;
}
}
return result;
}
bool InputDispatchPolicy::isScreenOn() {
JNIEnv* env = threadEnv();
jboolean result = env->CallBooleanMethod(mCallbacks, gCallbacksClassInfo.isScreenOn);
if (checkException(env, "isScreenOn")) {
return true;
}
return result;
}
bool InputDispatchPolicy::isScreenBright() {
JNIEnv* env = threadEnv();
jboolean result = env->CallBooleanMethod(mCallbacks, gCallbacksClassInfo.isScreenBright);
if (checkException(env, "isScreenBright")) {
return true;
}
return result;
}
void InputDispatchPolicy::notifyConfigurationChanged(nsecs_t when,
int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) {
JNIEnv* env = threadEnv();
env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyConfigurationChanged,
when, touchScreenConfig, keyboardConfig, navigationConfig);
checkException(env, "notifyConfigurationChanged");
}
void InputDispatchPolicy::notifyLidSwitchChanged(nsecs_t when, bool lidOpen) {
JNIEnv* env = threadEnv();
env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyLidSwitchChanged,
when, lidOpen);
checkException(env, "notifyLidSwitchChanged");
}
void InputDispatchPolicy::virtualKeyFeedback(nsecs_t when, int32_t deviceId,
int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime) {
JNIEnv* env = threadEnv();
env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.virtualKeyFeedback,
when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
checkException(env, "virtualKeyFeedback");
}
int32_t InputDispatchPolicy::interceptKey(nsecs_t when,
int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
const int32_t WM_ACTION_PASS_TO_USER = 1;
const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
const int32_t WM_ACTION_GO_TO_SLEEP = 4;
JNIEnv* env = threadEnv();
bool isScreenOn = this->isScreenOn();
bool isScreenBright = this->isScreenBright();
jint wmActions = env->CallIntMethod(mCallbacks, gCallbacksClassInfo.hackInterceptKey,
deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
if (checkException(env, "hackInterceptKey")) {
wmActions = 0;
}
int32_t actions = ACTION_NONE;
if (! isScreenOn) {
// Key presses and releases wake the device.
actions |= ACTION_WOKE_HERE;
}
if (! isScreenBright) {
// Key presses and releases brighten the screen if dimmed.
actions |= ACTION_BRIGHT_HERE;
}
if (wmActions & WM_ACTION_GO_TO_SLEEP) {
env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.goToSleep, when);
checkException(env, "goToSleep");
}
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.pokeUserActivityForKey, when);
checkException(env, "pokeUserActivityForKey");
}
if (wmActions & WM_ACTION_PASS_TO_USER) {
actions |= ACTION_DISPATCH;
}
if (! (wmActions & WM_ACTION_PASS_TO_USER)) {
if (down && isAppSwitchKey(keyCode)) {
env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyAppSwitchComing);
checkException(env, "notifyAppSwitchComing");
actions |= ACTION_APP_SWITCH_COMING;
}
}
return actions;
}
int32_t InputDispatchPolicy::interceptTouch(nsecs_t when) {
if (! isScreenOn()) {
// Touch events do not wake the device.
return ACTION_NONE;
}
return ACTION_DISPATCH;
}
int32_t InputDispatchPolicy::interceptTrackball(nsecs_t when,
bool buttonChanged, bool buttonDown, bool rolled) {
if (! isScreenOn()) {
// Trackball motions and button presses do not wake the device.
return ACTION_NONE;
}
return ACTION_DISPATCH;
}
bool InputDispatchPolicy::filterTouchEvents() {
if (mFilterTouchEvents < 0) {
JNIEnv* env = threadEnv();
jboolean result = env->CallBooleanMethod(mCallbacks,
gCallbacksClassInfo.filterTouchEvents);
if (checkException(env, "filterTouchEvents")) {
result = false;
}
mFilterTouchEvents = result ? 1 : 0;
}
return mFilterTouchEvents;
}
bool InputDispatchPolicy::filterJumpyTouchEvents() {
if (mFilterJumpyTouchEvents < 0) {
JNIEnv* env = threadEnv();
jboolean result = env->CallBooleanMethod(mCallbacks,
gCallbacksClassInfo.filterJumpyTouchEvents);
if (checkException(env, "filterJumpyTouchEvents")) {
result = false;
}
mFilterJumpyTouchEvents = result ? 1 : 0;
}
return mFilterJumpyTouchEvents;
}
void InputDispatchPolicy::getVirtualKeyDefinitions(const String8& deviceName,
Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
JNIEnv* env = threadEnv();
jstring deviceNameStr = env->NewStringUTF(deviceName.string());
if (! checkException(env, "getVirtualKeyDefinitions")) {
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
if (! checkException(env, "getVirtualKeyDefinitions") && result) {
jsize length = env->GetArrayLength(result);
for (jsize i = 0; i < length; i++) {
jobject item = env->GetObjectArrayElement(result, i);
outVirtualKeyDefinitions.add();
outVirtualKeyDefinitions.editTop().scanCode =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
outVirtualKeyDefinitions.editTop().centerX =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
outVirtualKeyDefinitions.editTop().centerY =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
outVirtualKeyDefinitions.editTop().width =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
outVirtualKeyDefinitions.editTop().height =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
env->DeleteLocalRef(item);
}
env->DeleteLocalRef(result);
}
env->DeleteLocalRef(deviceNameStr);
}
}
void InputDispatchPolicy::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
JNIEnv* env = threadEnv();
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
gCallbacksClassInfo.getExcludedDeviceNames));
if (! checkException(env, "getExcludedDeviceNames") && result) {
jsize length = env->GetArrayLength(result);
for (jsize i = 0; i < length; i++) {
jstring item = jstring(env->GetObjectArrayElement(result, i));
const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
outExcludedDeviceNames.add(String8(deviceNameChars));
env->ReleaseStringUTFChars(item, deviceNameChars);
env->DeleteLocalRef(item);
}
env->DeleteLocalRef(result);
}
}
bool InputDispatchPolicy::allowKeyRepeat() {
// Disable key repeat when the screen is off.
return isScreenOn();
}
nsecs_t InputDispatchPolicy::getKeyRepeatTimeout() {
// TODO use ViewConfiguration.getLongPressTimeout()
return milliseconds_to_nanoseconds(500);
}
void InputDispatchPolicy::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
Vector<InputTarget>& outTargets) {
JNIEnv* env = threadEnv();
jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
if (! keyEventObj) {
LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
} else {
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
gCallbacksClassInfo.getKeyEventTargets,
keyEventObj, jint(keyEvent->getNature()), jint(policyFlags)));
if (! checkException(env, "getKeyEventTargets") && result) {
jsize length = env->GetArrayLength(result);
for (jsize i = 0; i < length; i++) {
jobject item = env->GetObjectArrayElement(result, i);
if (! item) {
break; // found null element indicating end of used portion of the array
}
outTargets.add();
android_view_InputTarget_toNative(env, item, & outTargets.editTop());
env->DeleteLocalRef(item);
}
env->DeleteLocalRef(result);
}
env->DeleteLocalRef(keyEventObj);
}
}
void InputDispatchPolicy::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
Vector<InputTarget>& outTargets) {
JNIEnv* env = threadEnv();
jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
if (! motionEventObj) {
LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
} else {
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
gCallbacksClassInfo.getMotionEventTargets,
motionEventObj, jint(motionEvent->getNature()), jint(policyFlags)));
if (! checkException(env, "getMotionEventTargets") && result) {
jsize length = env->GetArrayLength(result);
for (jsize i = 0; i < length; i++) {
jobject item = env->GetObjectArrayElement(result, i);
if (! item) {
break; // found null element indicating end of used portion of the array
}
outTargets.add();
android_view_InputTarget_toNative(env, item, & outTargets.editTop());
env->DeleteLocalRef(item);
}
env->DeleteLocalRef(result);
}
android_view_MotionEvent_recycle(env, motionEventObj);
env->DeleteLocalRef(motionEventObj);
}
}
} /* namespace android */

View File

@@ -156,7 +156,7 @@ android_server_KeyInputQueue_getSwitchState(JNIEnv* env, jobject clazz,
{
jint st = -1;
gLock.lock();
if (gHub != NULL) st = gHub->getSwitchState(sw);
if (gHub != NULL) st = gHub->getSwitchState(-1, -1, sw);
gLock.unlock();
return st;
@@ -168,7 +168,7 @@ android_server_KeyInputQueue_getSwitchStateDevice(JNIEnv* env, jobject clazz,
{
jint st = -1;
gLock.lock();
if (gHub != NULL) st = gHub->getSwitchState(deviceId, sw);
if (gHub != NULL) st = gHub->getSwitchState(deviceId, -1, sw);
gLock.unlock();
return st;
@@ -180,7 +180,7 @@ android_server_KeyInputQueue_getScancodeState(JNIEnv* env, jobject clazz,
{
jint st = -1;
gLock.lock();
if (gHub != NULL) st = gHub->getScancodeState(sw);
if (gHub != NULL) st = gHub->getScanCodeState(0, -1, sw);
gLock.unlock();
return st;
@@ -192,7 +192,7 @@ android_server_KeyInputQueue_getScancodeStateDevice(JNIEnv* env, jobject clazz,
{
jint st = -1;
gLock.lock();
if (gHub != NULL) st = gHub->getScancodeState(deviceId, sw);
if (gHub != NULL) st = gHub->getScanCodeState(deviceId, -1, sw);
gLock.unlock();
return st;
@@ -204,7 +204,7 @@ android_server_KeyInputQueue_getKeycodeState(JNIEnv* env, jobject clazz,
{
jint st = -1;
gLock.lock();
if (gHub != NULL) st = gHub->getKeycodeState(sw);
if (gHub != NULL) st = gHub->getKeyCodeState(0, -1, sw);
gLock.unlock();
return st;
@@ -216,7 +216,7 @@ android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz,
{
jint st = -1;
gLock.lock();
if (gHub != NULL) st = gHub->getKeycodeState(deviceId, sw);
if (gHub != NULL) st = gHub->getKeyCodeState(deviceId,-1, sw);
gLock.unlock();
return st;
@@ -247,7 +247,7 @@ android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz,
int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
size_t numCodes = env->GetArrayLength(keyCodes);
jsize numCodes = env->GetArrayLength(keyCodes);
if (numCodes == env->GetArrayLength(outFlags)) {
gLock.lock();
if (gHub != NULL) ret = gHub->hasKeys(numCodes, codes, flags);

View File

@@ -7,6 +7,7 @@ namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryService(JNIEnv* env);
int register_android_server_KeyInputQueue(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_SensorService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
@@ -28,6 +29,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
LOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_KeyInputQueue(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_BatteryService(env);

View File

@@ -46,6 +46,7 @@ import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.BridgeInflater;
import android.view.InputChannel;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.KeyEvent;
@@ -990,12 +991,20 @@ public final class Bridge implements ILayoutBridge {
private static final class WindowSession implements IWindowSession {
@SuppressWarnings("unused")
public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3)
public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3,
InputChannel outInputchannel)
throws RemoteException {
// pass for now.
return 0;
}
@SuppressWarnings("unused")
public int addWithoutInputChannel(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3)
throws RemoteException {
// pass for now.
return 0;
}
@SuppressWarnings("unused")
public void finishDrawing(IWindow arg0) throws RemoteException {
// pass for now.