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

Merge commit '7c8aa44f320f45e8417f0aba9ca67af6a67a5cf7' into gingerbread-plus-aosp

* commit '7c8aa44f320f45e8417f0aba9ca67af6a67a5cf7':
  Native input dispatch rewrite work in progress.
This commit is contained in:
Jeff Brown
2010-06-13 17:59:04 -07:00
committed by Android Git Automerger
75 changed files with 12279 additions and 791 deletions

View File

@@ -16,13 +16,11 @@
package android.os;
import java.util.ArrayList;
import android.util.AndroidRuntimeException;
import android.util.Config;
import android.util.Log;
import com.android.internal.os.RuntimeInit;
import java.util.ArrayList;
/**
* Low-level class holding the list of messages to be dispatched by a
@@ -34,11 +32,18 @@ import com.android.internal.os.RuntimeInit;
*/
public class MessageQueue {
Message mMessages;
private final ArrayList mIdleHandlers = new ArrayList();
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
private boolean mQuiting = false;
private int mObject = 0; // used by native code
boolean mQuitAllowed = true;
@SuppressWarnings("unused")
private int mPtr; // used by native code
private native void nativeInit();
private native void nativeDestroy();
private native boolean nativePollOnce(int timeoutMillis);
private native void nativeWake();
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
@@ -85,55 +90,39 @@ public class MessageQueue {
mIdleHandlers.remove(handler);
}
}
// Add an input pipe to the set being selected over. If token is
// negative, remove 'handler's entry from the current set and forget
// about it.
void setInputToken(int token, int region, Handler handler) {
if (token >= 0) nativeRegisterInputStream(token, region, handler);
else nativeUnregisterInputStream(token);
}
MessageQueue() {
nativeInit();
}
private native void nativeInit();
/**
* @param token fd of the readable end of the input stream
* @param region fd of the ashmem region used for data transport alongside the 'token' fd
* @param handler Handler from which to make input messages based on data read from the fd
*/
private native void nativeRegisterInputStream(int token, int region, Handler handler);
private native void nativeUnregisterInputStream(int token);
private native void nativeSignal();
/**
* Wait until the designated time for new messages to arrive.
*
* @param when Timestamp in SystemClock.uptimeMillis() base of the next message in the queue.
* If 'when' is zero, the method will check for incoming messages without blocking. If
* 'when' is negative, the method will block forever waiting for the next message.
* @return
*/
private native int nativeWaitForNext(long when);
@Override
protected void finalize() throws Throwable {
try {
nativeDestroy();
} finally {
super.finalize();
}
}
final Message next() {
boolean tryIdle = true;
// when we start out, we'll just touch the input pipes and then go from there
long timeToNextEventMillis = 0;
int timeToNextEventMillis = 0;
while (true) {
long now;
Object[] idlers = null;
nativeWaitForNext(timeToNextEventMillis);
boolean dispatched = nativePollOnce(timeToNextEventMillis);
// Try to retrieve the next message, returning if found.
synchronized (this) {
now = SystemClock.uptimeMillis();
Message msg = pullNextLocked(now);
if (msg != null) return msg;
if (msg != null) {
return msg;
}
if (tryIdle && mIdleHandlers.size() > 0) {
idlers = mIdleHandlers.toArray();
}
@@ -170,9 +159,14 @@ public class MessageQueue {
synchronized (this) {
// No messages, nobody to tell about it... time to wait!
if (mMessages != null) {
if (mMessages.when - now > 0) {
long longTimeToNextEventMillis = mMessages.when - now;
if (longTimeToNextEventMillis > 0) {
Binder.flushPendingCommands();
timeToNextEventMillis = mMessages.when - now;
timeToNextEventMillis = (int) Math.min(longTimeToNextEventMillis,
Integer.MAX_VALUE);
} else {
timeToNextEventMillis = 0;
}
} else {
Binder.flushPendingCommands();
@@ -230,7 +224,7 @@ public class MessageQueue {
msg.next = prev.next;
prev.next = msg;
}
nativeSignal();
nativeWake();
}
return true;
}
@@ -351,7 +345,7 @@ public class MessageQueue {
void poke()
{
synchronized (this) {
nativeSignal();
nativeWake();
}
}
}

View File

@@ -18,6 +18,7 @@ package android.service.wallpaper;
import com.android.internal.os.HandlerCaller;
import com.android.internal.view.BaseIWindow;
import com.android.internal.view.BaseInputHandler;
import com.android.internal.view.BaseSurfaceHolder;
import android.annotation.SdkConstant;
@@ -39,6 +40,10 @@ import android.util.Log;
import android.util.LogPrinter;
import android.view.Gravity;
import android.view.IWindowSession;
import android.view.InputChannel;
import android.view.InputHandler;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
@@ -46,6 +51,7 @@ import android.view.ViewGroup;
import android.view.ViewRoot;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
import java.util.ArrayList;
@@ -146,6 +152,7 @@ public abstract class WallpaperService extends Service {
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
IWindowSession mSession;
InputChannel mInputChannel;
final Object mLock = new Object();
boolean mOffsetMessageEnqueued;
@@ -205,6 +212,30 @@ public abstract class WallpaperService extends Service {
};
final InputHandler mInputHandler = new BaseInputHandler() {
@Override
public void handleTouch(MotionEvent event, Runnable finishedCallback) {
try {
synchronized (mLock) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (mPendingMove != null) {
mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
mPendingMove.recycle();
}
mPendingMove = event;
} else {
mPendingMove = null;
}
Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT,
event);
mCaller.sendMessage(msg);
}
} finally {
finishedCallback.run();
}
}
};
final BaseIWindow mWindow = new BaseIWindow() {
@Override
public boolean onDispatchPointer(MotionEvent event, long eventTime,
@@ -487,8 +518,15 @@ public abstract class WallpaperService extends Service {
mLayout.setTitle(WallpaperService.this.getClass().getName());
mLayout.windowAnimations =
com.android.internal.R.style.Animation_Wallpaper;
mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
mInputChannel = new InputChannel();
mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets,
mInputChannel);
mCreated = true;
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
InputQueue.registerInputChannel(mInputChannel, mInputHandler,
Looper.myQueue());
}
}
mSurfaceHolder.mSurfaceLock.lock();
@@ -587,6 +625,7 @@ public abstract class WallpaperService extends Service {
mSurfaceHolder.setSizeFromLayout();
mInitializing = true;
mSession = ViewRoot.getWindowSession(getMainLooper());
mWindow.setSession(mSession);
IntentFilter filter = new IntentFilter();
@@ -730,6 +769,15 @@ public abstract class WallpaperService extends Service {
try {
if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
+ mSurfaceHolder.getSurface() + " of: " + this);
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
if (mInputChannel != null) {
InputQueue.unregisterInputChannel(mInputChannel);
mInputChannel.dispose();
mInputChannel = null;
}
}
mSession.remove(mWindow);
} catch (RemoteException e) {
}

View File

@@ -21,6 +21,7 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.view.InputChannel;
import android.view.IWindow;
import android.view.MotionEvent;
import android.view.WindowManager;
@@ -33,6 +34,9 @@ import android.view.Surface;
*/
interface IWindowSession {
int add(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, out Rect outContentInsets,
out InputChannel outInputChannel);
int addWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, out Rect outContentInsets);
void remove(IWindow window);

View File

@@ -0,0 +1,20 @@
/* //device/java/android/android/view/InputChannel.aidl
**
** Copyright 2010, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
package android.view;
parcelable InputChannel;

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Slog;
/**
* An input channel specifies the file descriptors used to send input events to
* a window in another process. It is Parcelable so that it can be transmitted
* to the ViewRoot through a Binder transaction as part of registering the Window.
* @hide
*/
public class InputChannel implements Parcelable {
private static final String TAG = "InputChannel";
public static final Parcelable.Creator<InputChannel> CREATOR
= new Parcelable.Creator<InputChannel>() {
public InputChannel createFromParcel(Parcel source) {
InputChannel result = new InputChannel();
result.readFromParcel(source);
return result;
}
public InputChannel[] newArray(int size) {
return new InputChannel[size];
}
};
@SuppressWarnings("unused")
private int mPtr; // used by native code
private boolean mDisposeAfterWriteToParcel;
private static native InputChannel[] nativeOpenInputChannelPair(String name);
private native void nativeDispose(boolean finalized);
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
private native String nativeGetName();
/**
* Creates an uninitialized input channel.
* It can be initialized by reading from a Parcel or by transferring the state of
* another input channel into this one.
*/
public InputChannel() {
}
@Override
protected void finalize() throws Throwable {
try {
nativeDispose(true);
} finally {
super.finalize();
}
}
/**
* Creates a new input channel pair. One channel should be provided to the input
* dispatcher and the other to the application's input queue.
* @param name The descriptive (non-unique) name of the channel pair.
* @return A pair of input channels. They are symmetric and indistinguishable.
*/
public static InputChannel[] openInputChannelPair(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
Slog.d(TAG, "Opening input channel pair '" + name + "'");
return nativeOpenInputChannelPair(name);
}
/**
* Gets the name of the input channel.
* @return The input channel name.
*/
public String getName() {
String name = nativeGetName();
return name != null ? name : "uninitialized";
}
/**
* Disposes the input channel.
* Explicitly releases the reference this object is holding on the input channel.
* When all references are released, the input channel will be closed.
*/
public void dispose() {
nativeDispose(false);
}
/**
* Transfers ownership of the internal state of the input channel to another
* instance and invalidates this instance. This is used to pass an input channel
* as an out parameter in a binder call.
* @param other The other input channel instance.
*/
public void transferToBinderOutParameter(InputChannel outParameter) {
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
nativeTransferTo(outParameter);
outParameter.mDisposeAfterWriteToParcel = true;
}
public int describeContents() {
return Parcelable.CONTENTS_FILE_DESCRIPTOR;
}
public void readFromParcel(Parcel in) {
if (in == null) {
throw new IllegalArgumentException("in must not be null");
}
nativeReadFromParcel(in);
}
public void writeToParcel(Parcel out, int flags) {
if (out == null) {
throw new IllegalArgumentException("out must not be null");
}
nativeWriteToParcel(out);
if (mDisposeAfterWriteToParcel) {
dispose();
}
}
@Override
public String toString() {
return getName();
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
/**
* Handles input messages that arrive on an input channel.
* @hide
*/
public interface InputHandler {
/**
* Handle a key event.
* It is the responsibility of the callee to ensure that the finished callback is
* eventually invoked when the event processing is finished and the input system
* can send the next event.
* @param event The key event data.
* @param finishedCallback The callback to invoke when event processing is finished.
*/
public void handleKey(KeyEvent event, Runnable finishedCallback);
/**
* Handle a touch event.
* It is the responsibility of the callee to ensure that the finished callback is
* eventually invoked when the event processing is finished and the input system
* can send the next event.
* @param event The motion event data.
* @param finishedCallback The callback to invoke when event processing is finished.
*/
public void handleTouch(MotionEvent event, Runnable finishedCallback);
/**
* Handle a trackball event.
* It is the responsibility of the callee to ensure that the finished callback is
* eventually invoked when the event processing is finished and the input system
* can send the next event.
* @param event The motion event data.
* @param finishedCallback The callback to invoke when event processing is finished.
*/
public void handleTrackball(MotionEvent event, Runnable finishedCallback);
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.os.MessageQueue;
import android.util.Slog;
/**
* An input queue provides a mechanism for an application to receive incoming
* input events sent over an input channel. Signalling is implemented by MessageQueue.
* @hide
*/
public final class InputQueue {
private static final String TAG = "InputQueue";
// Describes the interpretation of an event.
// XXX This concept is tentative. See comments in android/input.h.
public static final int INPUT_EVENT_NATURE_KEY = 1;
public static final int INPUT_EVENT_NATURE_TOUCH = 2;
public static final int INPUT_EVENT_NATURE_TRACKBALL = 3;
private static Object sLock = new Object();
private static native void nativeRegisterInputChannel(InputChannel inputChannel,
InputHandler inputHandler, MessageQueue messageQueue);
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
private static native void nativeFinished(long finishedToken);
private InputQueue() {
}
/**
* Registers an input channel and handler.
* @param inputChannel The input channel to register.
* @param inputHandler The input handler to input events send to the target.
* @param messageQueue The message queue on whose thread the handler should be invoked.
*/
public static void registerInputChannel(InputChannel inputChannel, InputHandler inputHandler,
MessageQueue messageQueue) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (inputHandler == null) {
throw new IllegalArgumentException("inputHandler must not be null");
}
if (messageQueue == null) {
throw new IllegalArgumentException("messageQueue must not be null");
}
synchronized (sLock) {
Slog.d(TAG, "Registering input channel '" + inputChannel + "'");
nativeRegisterInputChannel(inputChannel, inputHandler, messageQueue);
}
}
/**
* Unregisters an input channel.
* Does nothing if the channel is not currently registered.
* @param inputChannel The input channel to unregister.
*/
public static void unregisterInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
synchronized (sLock) {
Slog.d(TAG, "Unregistering input channel '" + inputChannel + "'");
nativeUnregisterInputChannel(inputChannel);
}
}
@SuppressWarnings("unused")
private static void dispatchKeyEvent(InputHandler inputHandler,
KeyEvent event, int nature, long finishedToken) {
Runnable finishedCallback = new FinishedCallback(finishedToken);
if (nature == INPUT_EVENT_NATURE_KEY) {
inputHandler.handleKey(event, finishedCallback);
} else {
Slog.d(TAG, "Unsupported nature for key event: " + nature);
}
}
@SuppressWarnings("unused")
private static void dispatchMotionEvent(InputHandler inputHandler,
MotionEvent event, int nature, long finishedToken) {
Runnable finishedCallback = new FinishedCallback(finishedToken);
if (nature == INPUT_EVENT_NATURE_TOUCH) {
inputHandler.handleTouch(event, finishedCallback);
} else if (nature == INPUT_EVENT_NATURE_TRACKBALL) {
inputHandler.handleTrackball(event, finishedCallback);
} else {
Slog.d(TAG, "Unsupported nature for motion event: " + nature);
}
}
// TODO consider recycling finished callbacks when done
private static class FinishedCallback implements Runnable {
private long mFinishedToken;
public FinishedCallback(long finishedToken) {
mFinishedToken = finishedToken;
}
public void run() {
synchronized (sLock) {
nativeFinished(mFinishedToken);
}
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
/**
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
* window area.
*
* These parameters are used by the native input dispatching code.
* @hide
*/
public class InputTarget {
public InputChannel mInputChannel;
public int mFlags;
public long mTimeoutNanos;
public float mXOffset;
public float mYOffset;
/**
* This flag indicates that subsequent event delivery should be held until the
* current event is delivered to this target or a timeout occurs.
*/
public static int FLAG_SYNC = 0x01;
/**
* This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
* this target and so should instead be delivered as an ACTION_OUTSIDE to this target.
*/
public static int FLAG_OUTSIDE = 0x02;
/*
* This flag indicates that a KeyEvent or MotionEvent is being canceled.
* In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
* In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL.
*/
public static int FLAG_CANCEL = 0x04;
public void recycle() {
mInputChannel = null;
}
}

View File

@@ -127,6 +127,7 @@ public class KeyEvent implements Parcelable {
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
// native/include/android/keycodes.h
// frameworks/base/include/ui/KeycodeLabels.h
// tools/puppet_master/PuppetMaster/nav_keys.py
// frameworks/base/core/res/res/values/attrs.xml
@@ -162,7 +163,7 @@ public class KeyEvent implements Parcelable {
* key code is not {#link {@link #KEYCODE_UNKNOWN} then the
* {#link {@link #getRepeatCount()} method returns the number of times
* the given key code should be executed.
* Otherwise, if the key code {@link #KEYCODE_UNKNOWN}, then
* Otherwise, if the key code is {@link #KEYCODE_UNKNOWN}, then
* this is a sequence of characters as returned by {@link #getCharacters}.
*/
public static final int ACTION_MULTIPLE = 2;
@@ -330,7 +331,7 @@ public class KeyEvent implements Parcelable {
private int mMetaState;
private int mAction;
private int mKeyCode;
private int mScancode;
private int mScanCode;
private int mRepeatCount;
private int mDeviceId;
private int mFlags;
@@ -480,7 +481,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = repeat;
mMetaState = metaState;
mDeviceId = device;
mScancode = scancode;
mScanCode = scancode;
}
/**
@@ -510,7 +511,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = repeat;
mMetaState = metaState;
mDeviceId = device;
mScancode = scancode;
mScanCode = scancode;
mFlags = flags;
}
@@ -547,7 +548,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = origEvent.mRepeatCount;
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
mScancode = origEvent.mScancode;
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
mCharacters = origEvent.mCharacters;
}
@@ -572,7 +573,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = newRepeat;
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
mScancode = origEvent.mScancode;
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
mCharacters = origEvent.mCharacters;
}
@@ -625,7 +626,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = origEvent.mRepeatCount;
mMetaState = origEvent.mMetaState;
mDeviceId = origEvent.mDeviceId;
mScancode = origEvent.mScancode;
mScanCode = origEvent.mScanCode;
mFlags = origEvent.mFlags;
// Don't copy mCharacters, since one way or the other we'll lose it
// when changing the action.
@@ -859,7 +860,7 @@ public class KeyEvent implements Parcelable {
* Mostly this is here for debugging purposes.
*/
public final int getScanCode() {
return mScancode;
return mScanCode;
}
/**
@@ -1183,7 +1184,7 @@ public class KeyEvent implements Parcelable {
public String toString() {
return "KeyEvent{action=" + mAction + " code=" + mKeyCode
+ " repeat=" + mRepeatCount
+ " meta=" + mMetaState + " scancode=" + mScancode
+ " meta=" + mMetaState + " scancode=" + mScanCode
+ " mFlags=" + mFlags + "}";
}
@@ -1208,7 +1209,7 @@ public class KeyEvent implements Parcelable {
out.writeInt(mRepeatCount);
out.writeInt(mMetaState);
out.writeInt(mDeviceId);
out.writeInt(mScancode);
out.writeInt(mScanCode);
out.writeInt(mFlags);
out.writeLong(mDownTime);
out.writeLong(mEventTime);
@@ -1220,7 +1221,7 @@ public class KeyEvent implements Parcelable {
mRepeatCount = in.readInt();
mMetaState = in.readInt();
mDeviceId = in.readInt();
mScancode = in.readInt();
mScanCode = in.readInt();
mFlags = in.readInt();
mDownTime = in.readLong();
mEventTime = in.readLong();

View File

@@ -248,17 +248,17 @@ public final class MotionEvent implements Parcelable {
private RuntimeException mRecycledLocation;
private boolean mRecycled;
private MotionEvent() {
mPointerIdentifiers = new int[BASE_AVAIL_POINTERS];
mDataSamples = new float[BASE_AVAIL_POINTERS*BASE_AVAIL_SAMPLES*NUM_SAMPLE_DATA];
mTimeSamples = new long[BASE_AVAIL_SAMPLES];
private MotionEvent(int pointerCount, int sampleCount) {
mPointerIdentifiers = new int[pointerCount];
mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
mTimeSamples = new long[sampleCount];
}
static private MotionEvent obtain() {
final MotionEvent ev;
synchronized (gRecyclerLock) {
if (gRecyclerTop == null) {
return new MotionEvent();
return new MotionEvent(BASE_AVAIL_POINTERS, BASE_AVAIL_SAMPLES);
}
ev = gRecyclerTop;
gRecyclerTop = ev.mNext;
@@ -269,6 +269,45 @@ public final class MotionEvent implements Parcelable {
ev.mNext = null;
return ev;
}
@SuppressWarnings("unused") // used by native code
static private MotionEvent obtain(int pointerCount, int sampleCount) {
final MotionEvent ev;
synchronized (gRecyclerLock) {
if (gRecyclerTop == null) {
if (pointerCount < BASE_AVAIL_POINTERS) {
pointerCount = BASE_AVAIL_POINTERS;
}
if (sampleCount < BASE_AVAIL_SAMPLES) {
sampleCount = BASE_AVAIL_SAMPLES;
}
return new MotionEvent(pointerCount, sampleCount);
}
ev = gRecyclerTop;
gRecyclerTop = ev.mNext;
gRecyclerUsed--;
}
ev.mRecycledLocation = null;
ev.mRecycled = false;
ev.mNext = null;
if (ev.mPointerIdentifiers.length < pointerCount) {
ev.mPointerIdentifiers = new int[pointerCount];
}
final int timeSamplesLength = ev.mTimeSamples.length;
if (timeSamplesLength < sampleCount) {
ev.mTimeSamples = new long[sampleCount];
}
final int dataSamplesLength = ev.mDataSamples.length;
final int neededDataSamplesLength = pointerCount * sampleCount * NUM_SAMPLE_DATA;
if (dataSamplesLength < neededDataSamplesLength) {
ev.mDataSamples = new float[neededDataSamplesLength];
}
return ev;
}
/**
* Create a new MotionEvent, filling in all of the basic values that
@@ -1022,7 +1061,7 @@ public final class MotionEvent implements Parcelable {
}
/**
* Returns a bitfield indicating which edges, if any, where touched by this
* Returns a bitfield indicating which edges, if any, were touched by this
* MotionEvent. For touch events, clients can use this to determine if the
* user's finger was touching the edge of the display.
*

View File

@@ -471,7 +471,7 @@ public class SurfaceView extends View {
mWindow = new MyWindow(this);
mLayout.type = mWindowType;
mLayout.gravity = Gravity.LEFT|Gravity.TOP;
mSession.add(mWindow, mLayout,
mSession.addWithoutInputChannel(mWindow, mLayout,
mVisible ? VISIBLE : GONE, mContentInsets);
}

View File

@@ -153,6 +153,7 @@ public final class ViewRoot extends Handler implements ViewParent,
CompatibilityInfo.Translator mTranslator;
final View.AttachInfo mAttachInfo;
InputChannel mInputChannel;
final Rect mTempRect; // used in the transaction to not thrash the heap.
final Rect mVisRect; // used to retrieve visible rect of focused view.
@@ -219,7 +220,7 @@ public final class ViewRoot extends Handler implements ViewParent,
AudioManager mAudioManager;
private final int mDensity;
public static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
@@ -434,9 +435,6 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
// fd [0] is the receiver, [1] is the sender
private native int[] makeInputChannel();
/**
* We have one child
*/
@@ -488,25 +486,20 @@ public final class ViewRoot extends Handler implements ViewParent,
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Set up the input event channel
if (false) {
int[] fds = makeInputChannel();
if (DEBUG_INPUT) {
Log.v(TAG, "makeInputChannel() returned " + fds);
}
}
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
mInputChannel = new InputChannel();
try {
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets);
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
} finally {
@@ -514,7 +507,7 @@ public final class ViewRoot extends Handler implements ViewParent,
attrs.restore();
}
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
@@ -560,6 +553,12 @@ public final class ViewRoot extends Handler implements ViewParent,
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
InputQueue.registerInputChannel(mInputChannel, mInputHandler,
Looper.myQueue());
}
view.assignParent(this);
mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
@@ -1735,6 +1734,14 @@ public final class ViewRoot extends Handler implements ViewParent,
}
mSurface.release();
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
if (mInputChannel != null) {
InputQueue.unregisterInputChannel(mInputChannel);
mInputChannel.dispose();
mInputChannel = null;
}
}
try {
sWindowSession.remove(mWindow);
} catch (RemoteException e) {
@@ -1841,19 +1848,16 @@ public final class ViewRoot extends Handler implements ViewParent,
boolean callWhenDone = msg.arg1 != 0;
if (event == null) {
try {
long timeBeforeGettingEvents;
if (MEASURE_LATENCY) {
timeBeforeGettingEvents = System.nanoTime();
}
long timeBeforeGettingEvents;
if (MEASURE_LATENCY) {
timeBeforeGettingEvents = System.nanoTime();
}
event = sWindowSession.getPendingPointerMove(mWindow);
event = getPendingPointerMotionEvent();
if (MEASURE_LATENCY && event != null) {
lt.sample("9 Client got events ", System.nanoTime() - event.getEventTimeNano());
lt.sample("8 Client getting events ", timeBeforeGettingEvents - event.getEventTimeNano());
}
} catch (RemoteException e) {
if (MEASURE_LATENCY && event != null) {
lt.sample("9 Client got events ", System.nanoTime() - event.getEventTimeNano());
lt.sample("8 Client getting events ", timeBeforeGettingEvents - event.getEventTimeNano());
}
callWhenDone = false;
}
@@ -1928,14 +1932,9 @@ public final class ViewRoot extends Handler implements ViewParent,
}
} finally {
if (callWhenDone) {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
}
if (event != null) {
event.recycle();
finishMotionEvent();
}
recycleMotionEvent(event);
if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
// Let the exception fall through -- the looper will catch
// it and take care of the bad app for us.
@@ -2075,7 +2074,63 @@ public final class ViewRoot extends Handler implements ViewParent,
} break;
}
}
private void finishKeyEvent(KeyEvent event) {
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
if (mFinishedCallback != null) {
mFinishedCallback.run();
mFinishedCallback = null;
}
} else {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
}
}
private void finishMotionEvent() {
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
throw new IllegalStateException("Should not be reachable with native input dispatch.");
}
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
}
private void recycleMotionEvent(MotionEvent event) {
if (event != null) {
event.recycle();
}
}
private MotionEvent getPendingPointerMotionEvent() {
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
throw new IllegalStateException("Should not be reachable with native input dispatch.");
}
try {
return sWindowSession.getPendingPointerMove(mWindow);
} catch (RemoteException e) {
return null;
}
}
private MotionEvent getPendingTrackballMotionEvent() {
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
throw new IllegalStateException("Should not be reachable with native input dispatch.");
}
try {
return sWindowSession.getPendingTrackballMove(mWindow);
} catch (RemoteException e) {
return null;
}
}
/**
* Something in the current window tells us we need to change the touch mode. For
* example, we are not in touch mode, and the user touches the screen.
@@ -2200,10 +2255,7 @@ public final class ViewRoot extends Handler implements ViewParent,
private void deliverTrackballEvent(MotionEvent event, boolean callWhenDone) {
if (event == null) {
try {
event = sWindowSession.getPendingTrackballMove(mWindow);
} catch (RemoteException e) {
}
event = getPendingTrackballMotionEvent();
callWhenDone = false;
}
@@ -2223,14 +2275,9 @@ public final class ViewRoot extends Handler implements ViewParent,
} finally {
if (handled) {
if (callWhenDone) {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
}
if (event != null) {
event.recycle();
finishMotionEvent();
}
recycleMotionEvent(event);
// If we reach this, we delivered a trackball event to mView and
// mView consumed it. Because we will not translate the trackball
// event into a key event, touch mode will not exit, so we exit
@@ -2339,13 +2386,8 @@ public final class ViewRoot extends Handler implements ViewParent,
}
} finally {
if (callWhenDone) {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
if (event != null) {
event.recycle();
}
finishMotionEvent();
recycleMotionEvent(event);
}
// Let the exception fall through -- the looper will catch
// it and take care of the bad app for us.
@@ -2503,10 +2545,7 @@ public final class ViewRoot extends Handler implements ViewParent,
if (sendDone) {
if (LOCAL_LOGV) Log.v(
"ViewRoot", "Telling window manager key is finished");
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
finishKeyEvent(event);
}
return;
}
@@ -2539,10 +2578,7 @@ public final class ViewRoot extends Handler implements ViewParent,
} else if (sendDone) {
if (LOCAL_LOGV) Log.v(
"ViewRoot", "Telling window manager key is finished");
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
finishKeyEvent(event);
} else {
Log.w("ViewRoot", "handleFinishedEvent(seq=" + seq
+ " handled=" + handled + " ev=" + event
@@ -2617,10 +2653,7 @@ public final class ViewRoot extends Handler implements ViewParent,
if (sendDone) {
if (LOCAL_LOGV) Log.v(
"ViewRoot", "Telling window manager key is finished");
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
finishKeyEvent(event);
}
// Let the exception fall through -- the looper will catch
// it and take care of the bad app for us.
@@ -2798,6 +2831,53 @@ public final class ViewRoot extends Handler implements ViewParent,
msg.obj = ri;
sendMessage(msg);
}
private Runnable mFinishedCallback;
private final InputHandler mInputHandler = new InputHandler() {
public void handleKey(KeyEvent event, Runnable finishedCallback) {
mFinishedCallback = finishedCallback;
if (event.getAction() == KeyEvent.ACTION_DOWN) {
//noinspection ConstantConditions
if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
if (Config.LOGD) Log.d("keydisp",
"===================================================");
if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:");
debug();
if (Config.LOGD) Log.d("keydisp",
"===================================================");
}
}
Message msg = obtainMessage(DISPATCH_KEY);
msg.obj = event;
if (LOCAL_LOGV) Log.v(
"ViewRoot", "sending key " + event + " to " + mView);
sendMessageAtTime(msg, event.getEventTime());
}
public void handleTouch(MotionEvent event, Runnable finishedCallback) {
finishedCallback.run();
Message msg = obtainMessage(DISPATCH_POINTER);
msg.obj = event;
msg.arg1 = 0;
sendMessageAtTime(msg, event.getEventTime());
}
public void handleTrackball(MotionEvent event, Runnable finishedCallback) {
finishedCallback.run();
Message msg = obtainMessage(DISPATCH_TRACKBALL);
msg.obj = event;
msg.arg1 = 0;
sendMessageAtTime(msg, event.getEventTime());
}
};
public void dispatchKey(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -2968,7 +3048,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
static class EventCompletion extends Handler {
class EventCompletion extends Handler {
final IWindow mWindow;
final KeyEvent mKeyEvent;
final boolean mIsPointer;
@@ -2987,40 +3067,25 @@ public final class ViewRoot extends Handler implements ViewParent,
@Override
public void handleMessage(Message msg) {
if (mKeyEvent != null) {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
finishKeyEvent(mKeyEvent);
} else if (mIsPointer) {
boolean didFinish;
MotionEvent event = mMotionEvent;
if (event == null) {
try {
event = sWindowSession.getPendingPointerMove(mWindow);
} catch (RemoteException e) {
}
event = getPendingPointerMotionEvent();
didFinish = true;
} else {
didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
}
if (!didFinish) {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
finishMotionEvent();
}
} else {
MotionEvent event = mMotionEvent;
if (event == null) {
try {
event = sWindowSession.getPendingTrackballMove(mWindow);
} catch (RemoteException e) {
}
event = getPendingTrackballMotionEvent();
} else {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
}
finishMotionEvent();
}
}
}
@@ -3050,7 +3115,7 @@ public final class ViewRoot extends Handler implements ViewParent,
viewRoot.dispatchKey(event);
} else {
Log.w("ViewRoot.W", "Key event " + event + " but no ViewRoot available!");
new EventCompletion(mMainLooper, this, event, false, null);
viewRoot.new EventCompletion(mMainLooper, this, event, false, null);
}
}
@@ -3064,7 +3129,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
viewRoot.dispatchPointer(event, eventTime, callWhenDone);
} else {
new EventCompletion(mMainLooper, this, null, true, event);
viewRoot.new EventCompletion(mMainLooper, this, null, true, event);
}
}
@@ -3074,7 +3139,7 @@ public final class ViewRoot extends Handler implements ViewParent,
if (viewRoot != null) {
viewRoot.dispatchTrackball(event, eventTime, callWhenDone);
} else {
new EventCompletion(mMainLooper, this, null, false, event);
viewRoot.new EventCompletion(mMainLooper, this, null, false, event);
}
}

View File

@@ -78,6 +78,12 @@ public interface WindowManagerPolicy {
public final static int FLAG_BRIGHT_HERE = 0x20000000;
public final static boolean WATCH_POINTER = false;
/**
* Temporary flag added during the transition to the new native input dispatcher.
* This will be removed when the old input dispatch code is deleted.
*/
public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = false;
// flags for interceptKeyTq
/**
@@ -708,6 +714,8 @@ public interface WindowManagerPolicy {
*/
public boolean preprocessInputEventTq(RawInputEvent event);
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
/**
* Determine whether a given key code is used to cause an app switch
* to occur (most often the HOME key, also often ENDCALL). If you return