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:
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
20
core/java/android/view/InputChannel.aidl
Normal file
20
core/java/android/view/InputChannel.aidl
Normal 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;
|
152
core/java/android/view/InputChannel.java
Normal file
152
core/java/android/view/InputChannel.java
Normal 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();
|
||||
}
|
||||
}
|
53
core/java/android/view/InputHandler.java
Normal file
53
core/java/android/view/InputHandler.java
Normal 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);
|
||||
}
|
126
core/java/android/view/InputQueue.java
Normal file
126
core/java/android/view/InputQueue.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
core/java/android/view/InputTarget.java
Normal file
57
core/java/android/view/InputTarget.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
39
core/java/com/android/internal/view/BaseInputHandler.java
Normal file
39
core/java/com/android/internal/view/BaseInputHandler.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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 \
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
30
core/jni/android_os_MessageQueue.h
Normal file
30
core/jni/android_os_MessageQueue.h
Normal 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
|
288
core/jni/android_view_InputChannel.cpp
Normal file
288
core/jni/android_view_InputChannel.cpp
Normal 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
|
40
core/jni/android_view_InputChannel.h
Normal file
40
core/jni/android_view_InputChannel.h
Normal 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
|
471
core/jni/android_view_InputQueue.cpp
Normal file
471
core/jni/android_view_InputQueue.cpp
Normal 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
|
97
core/jni/android_view_InputTarget.cpp
Normal file
97
core/jni/android_view_InputTarget.cpp
Normal 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
|
31
core/jni/android_view_InputTarget.h
Normal file
31
core/jni/android_view_InputTarget.h
Normal 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
|
124
core/jni/android_view_KeyEvent.cpp
Normal file
124
core/jni/android_view_KeyEvent.cpp
Normal 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
|
35
core/jni/android_view_KeyEvent.h
Normal file
35
core/jni/android_view_KeyEvent.h
Normal 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
|
310
core/jni/android_view_MotionEvent.cpp
Normal file
310
core/jni/android_view_MotionEvent.cpp
Normal 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
|
38
core/jni/android_view_MotionEvent.h
Normal file
38
core/jni/android_view_MotionEvent.h
Normal 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
|
@ -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
4
include/private/README
Normal 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.
|
@ -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
310
include/ui/Input.h
Normal 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
|
198
include/ui/InputDispatchPolicy.h
Normal file
198
include/ui/InputDispatchPolicy.h
Normal 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
|
409
include/ui/InputDispatcher.h
Normal file
409
include/ui/InputDispatcher.h
Normal 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
134
include/ui/InputManager.h
Normal 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
472
include/ui/InputReader.h
Normal 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
331
include/ui/InputTransport.h
Normal 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
67
include/utils/BitSet.h
Normal 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
|
@ -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
137
include/utils/PollLoop.h
Normal 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
71
include/utils/Pool.h
Normal 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
|
@ -38,6 +38,8 @@ public:
|
||||
nsecs_t lap();
|
||||
nsecs_t elapsedTime() const;
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
const char* mName;
|
||||
int mClock;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
@ -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
238
libs/ui/Input.cpp
Normal 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
1315
libs/ui/InputDispatcher.cpp
Normal file
File diff suppressed because it is too large
Load Diff
114
libs/ui/InputManager.cpp
Normal file
114
libs/ui/InputManager.cpp
Normal 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
1844
libs/ui/InputReader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
684
libs/ui/InputTransport.cpp
Normal file
684
libs/ui/InputTransport.cpp
Normal 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
|
@ -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)
|
||||
|
19
libs/ui/tests/InputDispatcher_test.cpp
Normal file
19
libs/ui/tests/InputDispatcher_test.cpp
Normal 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
|
16
libs/ui/tests/region/Android.mk
Normal file
16
libs/ui/tests/region/Android.mk
Normal 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)
|
@ -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
267
libs/utils/PollLoop.cpp
Normal 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
37
libs/utils/Pool.cpp
Normal 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
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
@ -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)
|
||||
|
33
libs/utils/tests/Android.mk
Normal file
33
libs/utils/tests/Android.mk
Normal 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)) \
|
||||
)
|
398
libs/utils/tests/PollLoop_test.cpp
Normal file
398
libs/utils/tests/PollLoop_test.cpp
Normal 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
|
44
libs/utils/tests/TestHelpers.h
Normal file
44
libs/utils/tests/TestHelpers.h
Normal 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
|
500
native/include/android/input.h
Normal file
500
native/include/android/input.h
Normal 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
|
158
native/include/android/keycodes.h
Normal file
158
native/include/android/keycodes.h
Normal 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
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
460
services/java/com/android/server/InputManager.java
Normal file
460
services/java/com/android/server/InputManager.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
105
services/java/com/android/server/InputTargetList.java
Normal file
105
services/java/com/android/server/InputTargetList.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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 \
|
||||
|
746
services/jni/com_android_server_InputManager.cpp
Normal file
746
services/jni/com_android_server_InputManager.cpp
Normal 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 */
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Reference in New Issue
Block a user