am 46b9ac0a: Native input dispatch rewrite work in progress.

Merge commit '46b9ac0ae2162309774a7478cd9d4e578747bfc2' into gingerbread

* commit '46b9ac0ae2162309774a7478cd9d4e578747bfc2':
  Native input dispatch rewrite work in progress.
This commit is contained in:
Jeff Brown
2010-06-13 17:55:28 -07:00
committed by Android Git Automerger
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.
@ -86,54 +91,38 @@ public class MessageQueue {
}
}
// 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;
@ -270,6 +270,45 @@ public final class MotionEvent implements Parcelable {
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
* define the motion.
@ -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.
@ -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 {
@ -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.
@ -2076,6 +2075,62 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
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.
@ -2799,6 +2832,53 @@ public final class ViewRoot extends Handler implements ViewParent,
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) {
//noinspection ConstantConditions
@ -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

@ -79,6 +79,12 @@ public interface WindowManagerPolicy {
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
/**
* Pass this event to the user / app. To be returned from {@link #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>
#include "android_os_MessageQueue.h"
using namespace android;
namespace android {
// ----------------------------------------------------------------------------
static struct {
jclass mClass;
jclass clazz;
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;
jfieldID mPtr; // native object attached to the DVM MessageQueue
} gMessageQueueClassInfo;
// ----------------------------------------------------------------------------
static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
{
if (jniThrowException(env, exc, msg) != 0)
assert(false);
}
// ----------------------------------------------------------------------------
class MessageQueueNative {
class NativeMessageQueue {
public:
MessageQueueNative(int readSocket, int writeSocket);
~MessageQueueNative();
NativeMessageQueue();
~NativeMessageQueue();
// select on all FDs until the designated time; forever if wakeupTime is < 0
int waitForSignal(jobject mqueue, jlong wakeupTime);
inline sp<PollLoop> getPollLoop() { return mPollLoop; }
// 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(); }
bool pollOnce(int timeoutMillis);
void wake();
private:
struct InputPipe {
int fd;
int region;
jobject handler;
InputPipe() {}
InputPipe(int _fd, int _r, jobject _h) : fd(_fd), region(_r), handler(_h) {}
};
// consume an event from a socket, put it on the DVM MessageQueue indicated,
// and notify the other end of the pipe that we've consumed it.
void queueEventFromPipe(const InputPipe& pipe, jobject mqueue);
int mQueueReadFd, mQueueWriteFd;
Vector<InputPipe> mInputPipes;
sp<PollLoop> mPollLoop;
};
MessageQueueNative::MessageQueueNative(int readSocket, int writeSocket)
: mQueueReadFd(readSocket), mQueueWriteFd(writeSocket) {
// ----------------------------------------------------------------------------
NativeMessageQueue::NativeMessageQueue() {
mPollLoop = new PollLoop();
}
MessageQueueNative::~MessageQueueNative() {
NativeMessageQueue::~NativeMessageQueue() {
}
int MessageQueueNative::waitForSignal(jobject mqueue, jlong timeoutMillis) {
struct timeval tv, *timeout;
fd_set fdset;
bool NativeMessageQueue::pollOnce(int timeoutMillis) {
return mPollLoop->pollOnce(timeoutMillis);
}
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;
void NativeMessageQueue::wake() {
mPollLoop->wake();
}
// ----------------------------------------------------------------------------
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;
}
// always rebuild the fd set from scratch
FD_ZERO(&fdset);
android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
// 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;
}
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;
}
}
// now wait
int res = select(maxFd + 1, &fdset, NULL, NULL, timeout);
static void throwQueueNotInitialized(JNIEnv* env) {
jniThrowException(env, "java/lang/IllegalStateException", "Message queue not initialized");
}
// 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
}
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_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) {
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;
}
// 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;
}
// ----------------------------------------------------------------------------
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);
env->SetIntField(obj, gMessageQueueOffsets.mObject, (jint)mqn);
}
static void android_os_MessageQueue_signal(JNIEnv* env, jobject obj) {
MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject);
if (mqn != NULL) {
mqn->signalQueuePipe();
} else {
doThrow(env, "java/lang/IllegalStateException", "Queue not initialized");
}
}
static int android_os_MessageQueue_waitForNext(JNIEnv* env, jobject obj, jlong when) {
MessageQueueNative *mqn = (MessageQueueNative*) env->GetIntField(obj, gMessageQueueOffsets.mObject);
if (mqn != NULL) {
int res = mqn->waitForSignal(obj, when);
return res; // the DVM event, if any, has been constructed and queued now
}
return -1;
}
static void 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");
}
}
// ----------------------------------------------------------------------------
const char* const kKeyEventPathName = "android/view/KeyEvent";
const char* const kMessagePathName = "android/os/Message";
const char* const kMessageQueuePathName = "android/os/MessageQueue";
static JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()V", (void*)android_os_MessageQueue_init },
{ "nativeSignal", "()V", (void*)android_os_MessageQueue_signal },
{ "nativeWaitForNext", "(J)I", (void*)android_os_MessageQueue_waitForNext },
{ "nativeRegisterInputStream", "(IILandroid/os/Handler;)V", (void*)android_os_MessageQueue_registerInputStream },
{ "nativeUnregisterInputStream", "(I)V", (void*)android_os_MessageQueue_unregisterInputStream },
};
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,
gMessageQueueMethods, NELEM(gMessageQueueMethods));
}
}; // 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:
// 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;
// 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;
virtual uint32_t getDeviceClasses(int32_t deviceId) const;
String8 getDeviceName(int32_t deviceId) const;
virtual String8 getDeviceName(int32_t deviceId) const;
int getAbsoluteInfo(int32_t deviceId, int axis, int *outMinValue,
virtual 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,
virtual 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);
virtual void addExcludedDevice(const char* deviceName);
// special type codes when devices are added/removed.
enum {
DEVICE_ADDED = 0x10000000,
DEVICE_REMOVED = 0x20000000
};
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;
// examine key input devices for specific framework keycode support
bool hasKeys(size_t numCodes, int32_t* keyCodes, uint8_t* outFlags);
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

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

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;
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;
}
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::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);
device->layoutMap->findScancodes(keyCode, &scanCodes);
uint8_t key_bitmask[(KEY_MAX+7)/8];
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 KEY_STATE_UNKNOWN;
}
return 0;
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

@ -1642,6 +1642,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} */
public boolean isAppSwitchKeyTqTiLwLi(int keycode) {

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;
}
@ -1972,6 +1996,17 @@ public class WindowManagerService extends IWindowManager.Stub
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!
res = WindowManagerImpl.ADD_OKAY;
@ -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
@ -4990,6 +5077,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 {
@ -8425,6 +8818,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
String makeInputChannelName() {
return Integer.toHexString(System.identityHashCode(this))
+ " " + mAttrs.getTitle();
}
@Override
public String toString() {
return "Window{"
@ -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 {
@ -10805,6 +11204,21 @@ public class WindowManagerService extends IWindowManager.Stub
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) {
mAnimationPending = true;
@ -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,7 +991,15 @@ 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;