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