summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/InputManager.java460
-rw-r--r--services/java/com/android/server/InputTargetList.java105
-rw-r--r--services/java/com/android/server/KeyInputQueue.java8
-rw-r--r--services/java/com/android/server/WindowManagerService.java523
-rw-r--r--services/jni/Android.mk2
-rw-r--r--services/jni/com_android_server_InputManager.cpp746
-rw-r--r--services/jni/com_android_server_KeyInputQueue.cpp14
-rw-r--r--services/jni/onload.cpp2
8 files changed, 1799 insertions, 61 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) {
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index b90e327..499ca86 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -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 \
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
new file mode 100644
index 0000000..53262ae
--- /dev/null
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -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 */
diff --git a/services/jni/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp
index c92f8df..f9e3585 100644
--- a/services/jni/com_android_server_KeyInputQueue.cpp
+++ b/services/jni/com_android_server_KeyInputQueue.cpp
@@ -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);
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index d11e7e1..a1a6838 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -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);