summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2010-06-22 01:27:15 -0700
committerJeff Brown <jeffbrown@google.com>2010-06-28 19:10:54 -0700
commit349703effce5acc53ed96f7ed8556131f0c65e18 (patch)
tree359217d5076e3005c724b2117a59ffec81e7a83b
parentf2b544f5ae7676f7ab4cdf3379b2ed3c60a65def (diff)
downloadframeworks_base-349703effce5acc53ed96f7ed8556131f0c65e18.zip
frameworks_base-349703effce5acc53ed96f7ed8556131f0c65e18.tar.gz
frameworks_base-349703effce5acc53ed96f7ed8556131f0c65e18.tar.bz2
Native input event dispatching.
Target identification is now fully native. Fixed a couple of minor issues related to input injection. Native input enabled by default, can be disabled by setting WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH to false. Change-Id: I7edf66ed3e987cc9306ad4743ac57a116af452ff
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java11
-rw-r--r--core/java/android/view/InputTarget.java57
-rw-r--r--core/java/android/view/ViewRoot.java9
-rw-r--r--core/java/android/view/WindowManagerPolicy.java2
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_view_InputQueue.cpp4
-rw-r--r--core/jni/android_view_InputTarget.cpp97
-rw-r--r--include/ui/Input.h3
-rw-r--r--include/ui/InputDispatcher.h20
-rw-r--r--include/ui/InputManager.h10
-rw-r--r--include/ui/InputReader.h6
-rw-r--r--libs/ui/InputDispatcher.cpp86
-rw-r--r--libs/ui/InputManager.cpp4
-rw-r--r--libs/ui/InputReader.cpp55
-rw-r--r--libs/ui/InputTransport.cpp4
-rw-r--r--services/java/com/android/server/InputApplication.java (renamed from core/jni/android_view_InputTarget.h)30
-rw-r--r--services/java/com/android/server/InputManager.java92
-rw-r--r--services/java/com/android/server/InputTargetList.java104
-rw-r--r--services/java/com/android/server/InputWindow.java66
-rw-r--r--services/java/com/android/server/InputWindowList.java89
-rw-r--r--services/java/com/android/server/WindowManagerService.java925
-rw-r--r--services/jni/com_android_server_InputManager.cpp1368
23 files changed, 2135 insertions, 910 deletions
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 3f5d6ca..249ad62 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -773,8 +773,6 @@ public abstract class WallpaperService extends Service {
if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
if (mInputChannel != null) {
InputQueue.unregisterInputChannel(mInputChannel);
- mInputChannel.dispose();
- mInputChannel = null;
}
}
@@ -783,6 +781,15 @@ public abstract class WallpaperService extends Service {
}
mSurfaceHolder.mSurface.release();
mCreated = false;
+
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ // Dispose the input channel after removing the window so the Window Manager
+ // doesn't interpret the input channel being closed as an abnormal termination.
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
}
}
}
diff --git a/core/java/android/view/InputTarget.java b/core/java/android/view/InputTarget.java
deleted file mode 100644
index 6ff7305..0000000
--- a/core/java/android/view/InputTarget.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-/**
- * An input target specifies how an input event is to be dispatched to a particular window
- * including the window's input channel, control flags, a timeout, and an X / Y offset to
- * be added to input event coordinates to compensate for the absolute position of the
- * window area.
- *
- * These parameters are used by the native input dispatching code.
- * @hide
- */
-public final class InputTarget {
- public InputChannel mInputChannel;
- public int mFlags;
- public long mTimeoutNanos;
- public float mXOffset;
- public float mYOffset;
-
- /**
- * This flag indicates that subsequent event delivery should be held until the
- * current event is delivered to this target or a timeout occurs.
- */
- public static int FLAG_SYNC = 0x01;
-
- /**
- * This flag indicates that a MotionEvent with ACTION_DOWN falls outside of the area of
- * this target and so should instead be delivered as an ACTION_OUTSIDE to this target.
- */
- public static int FLAG_OUTSIDE = 0x02;
-
- /*
- * This flag indicates that a KeyEvent or MotionEvent is being canceled.
- * In the case of a key event, it should be delivered with KeyEvent.FLAG_CANCELED set.
- * In the case of a motion event, it should be delivered as MotionEvent.ACTION_CANCEL.
- */
- public static int FLAG_CANCEL = 0x04;
-
- public void recycle() {
- mInputChannel = null;
- }
-}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 4854190..1dc82e8 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1762,6 +1762,15 @@ public final class ViewRoot extends Handler implements ViewParent,
sWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
+
+ if (WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH) {
+ // Dispose the input channel after removing the window so the Window Manager
+ // doesn't interpret the input channel being closed as an abnormal termination.
+ if (mInputChannel != null) {
+ mInputChannel.dispose();
+ mInputChannel = null;
+ }
+ }
}
void updateConfiguration(Configuration config, boolean force) {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 431b786..be1f6d2 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -83,7 +83,7 @@ public interface WindowManagerPolicy {
* Temporary flag added during the transition to the new native input dispatcher.
* This will be removed when the old input dispatch code is deleted.
*/
- public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = false;
+ public final static boolean ENABLE_NATIVE_INPUT_DISPATCH = true;
// flags for interceptKeyTq
/**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d854e87..a008e96 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -47,7 +47,6 @@ LOCAL_SRC_FILES:= \
android_view_ViewRoot.cpp \
android_view_InputChannel.cpp \
android_view_InputQueue.cpp \
- android_view_InputTarget.cpp \
android_view_KeyEvent.cpp \
android_view_MotionEvent.cpp \
android_text_AndroidCharacter.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 3e69c78..6fb1369 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -164,7 +164,6 @@ extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
extern int register_android_view_InputChannel(JNIEnv* env);
extern int register_android_view_InputQueue(JNIEnv* env);
-extern int register_android_view_InputTarget(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
@@ -1297,7 +1296,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_app_NativeActivity),
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputQueue),
- REG_JNI(register_android_view_InputTarget),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
};
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index 63e00d7..6fb3cf7 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -19,10 +19,10 @@
//#define LOG_NDEBUG 0
// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 1
+#define DEBUG_DISPATCH_CYCLE 0
// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 1
+#define DEBUG_REGISTRATION 0
#include "JNIHelp.h"
diff --git a/core/jni/android_view_InputTarget.cpp b/core/jni/android_view_InputTarget.cpp
deleted file mode 100644
index a0a7054..0000000
--- a/core/jni/android_view_InputTarget.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "InputTarget-JNI"
-
-#include "JNIHelp.h"
-
-#include <utils/Log.h>
-#include <ui/InputDispatcher.h>
-#include <ui/InputTransport.h>
-#include "android_view_InputTarget.h"
-#include "android_view_InputChannel.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static struct {
- jclass clazz;
-
- jfieldID mInputChannel;
- jfieldID mFlags;
- jfieldID mTimeoutNanos;
- jfieldID mXOffset;
- jfieldID mYOffset;
-} gInputTargetClassInfo;
-
-// ----------------------------------------------------------------------------
-
-void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
- InputTarget* outInputTarget) {
- jobject inputChannelObj = env->GetObjectField(inputTargetObj,
- gInputTargetClassInfo.mInputChannel);
- jint flags = env->GetIntField(inputTargetObj,
- gInputTargetClassInfo.mFlags);
- jlong timeoutNanos = env->GetLongField(inputTargetObj,
- gInputTargetClassInfo.mTimeoutNanos);
- jfloat xOffset = env->GetFloatField(inputTargetObj,
- gInputTargetClassInfo.mXOffset);
- jfloat yOffset = env->GetFloatField(inputTargetObj,
- gInputTargetClassInfo.mYOffset);
-
- outInputTarget->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
- outInputTarget->flags = flags;
- outInputTarget->timeout = timeoutNanos;
- outInputTarget->xOffset = xOffset;
- outInputTarget->yOffset = yOffset;
-
- env->DeleteLocalRef(inputChannelObj);
-}
-
-// ----------------------------------------------------------------------------
-
-#define FIND_CLASS(var, className) \
- var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className); \
- var = jclass(env->NewGlobalRef(var));
-
-#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
- var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find field " fieldName);
-
-int register_android_view_InputTarget(JNIEnv* env) {
- FIND_CLASS(gInputTargetClassInfo.clazz, "android/view/InputTarget");
-
- GET_FIELD_ID(gInputTargetClassInfo.mInputChannel, gInputTargetClassInfo.clazz,
- "mInputChannel", "Landroid/view/InputChannel;");
-
- GET_FIELD_ID(gInputTargetClassInfo.mFlags, gInputTargetClassInfo.clazz,
- "mFlags", "I");
-
- GET_FIELD_ID(gInputTargetClassInfo.mTimeoutNanos, gInputTargetClassInfo.clazz,
- "mTimeoutNanos", "J");
-
- GET_FIELD_ID(gInputTargetClassInfo.mXOffset, gInputTargetClassInfo.clazz,
- "mXOffset", "F");
-
- GET_FIELD_ID(gInputTargetClassInfo.mYOffset, gInputTargetClassInfo.clazz,
- "mYOffset", "F");
-
- return 0;
-}
-
-} // namespace android
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 32f85b3..57b292b 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -87,6 +87,9 @@ enum {
// Indicates that the screen was dim when the event was received and the event
// should brighten the device.
POLICY_FLAG_BRIGHT_HERE = 0x20000000,
+
+ // Indicates that the dispatcher should call back into the policy before dispatching. */
+ POLICY_FLAG_INTERCEPT_DISPATCH = 0x40000000,
};
/*
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 511ad20..eb8f820 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -126,21 +126,21 @@ public:
/* Gets the key repeat timeout or -1 if automatic key repeating is disabled. */
virtual nsecs_t getKeyRepeatTimeout() = 0;
- /* Gets the input targets for a key event.
+ /* Waits for key event input targets to become available.
* If the event is being injected, injectorPid and injectorUid should specify the
* process id and used id of the injecting application, otherwise they should both
* be -1.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
- virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) = 0;
- /* Gets the input targets for a motion event.
+ /* Waits for motion event targets to become available.
* If the event is being injected, injectorPid and injectorUid should specify the
* process id and used id of the injecting application, otherwise they should both
* be -1.
* Returns one of the INPUT_EVENT_INJECTION_XXX constants. */
- virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) = 0;
};
@@ -186,6 +186,16 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+ /* Preempts input dispatch in progress by making pending synchronous
+ * dispatches asynchronous instead. This method is generally called during a focus
+ * transition from one application to the next so as to enable the new application
+ * to start receiving input as soon as possible without having to wait for the
+ * old application to finish up.
+ *
+ * This method may be called on any thread (usually by the input manager).
+ */
+ virtual void preemptInputDispatch() = 0;
+
/* Registers or unregister input channels that may be used as targets for input events.
*
* These methods may be called on any thread (usually by the input manager).
@@ -233,6 +243,8 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+ virtual void preemptInputDispatch();
+
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h
index 7509dd2..e755238 100644
--- a/include/ui/InputManager.h
+++ b/include/ui/InputManager.h
@@ -87,6 +87,14 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
+ /* Preempts input dispatch in progress by making pending synchronous
+ * dispatches asynchronous instead. This method is generally called during a focus
+ * transition from one application to the next so as to enable the new application
+ * to start receiving input as soon as possible without having to wait for the
+ * old application to finish up.
+ */
+ virtual void preemptInputDispatch() = 0;
+
/* Gets input device configuration. */
virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
@@ -130,6 +138,8 @@ public:
virtual int32_t injectInputEvent(const InputEvent* event,
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
+ virtual void preemptInputDispatch();
+
virtual void getInputConfiguration(InputConfiguration* outConfiguration) const;
virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
int32_t scanCode) const;
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index d76b8fe..2093560 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -361,7 +361,11 @@ public:
// The input dispatcher should add POLICY_FLAG_BRIGHT_HERE to the policy flags it
// passes through the dispatch pipeline.
- ACTION_BRIGHT_HERE = 0x00000008
+ ACTION_BRIGHT_HERE = 0x00000008,
+
+ // The input dispatcher should add POLICY_FLAG_INTERCEPT_DISPATCH to the policy flags
+ // it passed through the dispatch pipeline.
+ ACTION_INTERCEPT_DISPATCH = 0x00000010
};
/* Describes a virtual key. */
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index b3103a4..0fc29b2 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -8,25 +8,25 @@
//#define LOG_NDEBUG 0
// Log detailed debug messages about each inbound event notification to the dispatcher.
-#define DEBUG_INBOUND_EVENT_DETAILS 1
+#define DEBUG_INBOUND_EVENT_DETAILS 0
// Log detailed debug messages about each outbound event processed by the dispatcher.
-#define DEBUG_OUTBOUND_EVENT_DETAILS 1
+#define DEBUG_OUTBOUND_EVENT_DETAILS 0
// Log debug messages about batching.
-#define DEBUG_BATCHING 1
+#define DEBUG_BATCHING 0
// Log debug messages about the dispatch cycle.
-#define DEBUG_DISPATCH_CYCLE 1
+#define DEBUG_DISPATCH_CYCLE 0
// Log debug messages about registrations.
-#define DEBUG_REGISTRATION 1
+#define DEBUG_REGISTRATION 0
// Log debug messages about performance statistics.
-#define DEBUG_PERFORMANCE_STATISTICS 1
+#define DEBUG_PERFORMANCE_STATISTICS 0
// Log debug messages about input event injection.
-#define DEBUG_INJECTION 1
+#define DEBUG_INJECTION 0
#include <cutils/log.h>
#include <ui/InputDispatcher.h>
@@ -249,9 +249,7 @@ void InputDispatcher::processKeyLockedInterruptible(
entry->downTime);
#endif
- // TODO: Poke user activity.
-
- if (entry->action == KEY_EVENT_ACTION_DOWN) {
+ if (entry->action == KEY_EVENT_ACTION_DOWN && ! entry->isInjected()) {
if (mKeyRepeatState.lastKeyEntry
&& mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
// We have seen two identical key downs in a row which indicates that the device
@@ -277,14 +275,24 @@ void InputDispatcher::processKeyLockedInterruptible(
void InputDispatcher::processKeyRepeatLockedInterruptible(
nsecs_t currentTime, nsecs_t keyRepeatTimeout) {
- // TODO Old WindowManagerServer code sniffs the input queue for following key up
- // events and drops the repeat if one is found. We should do something similar.
- // One good place to do it is in notifyKey as soon as the key up enters the
- // inbound event queue.
+ KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+
+ // Search the inbound queue for a key up corresponding to this device.
+ // It doesn't make sense to generate a key repeat event if the key is already up.
+ for (EventEntry* queuedEntry = mInboundQueue.head.next;
+ queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) {
+ if (queuedEntry->type == EventEntry::TYPE_KEY) {
+ KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry);
+ if (queuedKeyEntry->deviceId == entry->deviceId
+ && entry->action == KEY_EVENT_ACTION_UP) {
+ resetKeyRepeatLocked();
+ return;
+ }
+ }
+ }
// Synthesize a key repeat after the repeat timeout expired.
- // We reuse the previous key entry if otherwise unreferenced.
- KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+ // Reuse the repeated key entry if it is otherwise unreferenced.
uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
if (entry->refCount == 1) {
entry->eventTime = currentTime;
@@ -366,7 +374,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
entry->downTime, entry->eventTime);
mCurrentInputTargets.clear();
- int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent,
+ int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent,
entry->policyFlags, entry->injectorPid, entry->injectorUid,
mCurrentInputTargets);
@@ -375,7 +383,9 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
setInjectionResultLocked(entry, injectionResult);
- dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ }
}
void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
@@ -395,7 +405,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
entry->firstSample.pointerCoords);
mCurrentInputTargets.clear();
- int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent,
+ int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent,
entry->policyFlags, entry->injectorPid, entry->injectorUid,
mCurrentInputTargets);
@@ -404,7 +414,9 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
setInjectionResultLocked(entry, injectionResult);
- dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
+ }
}
void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime,
@@ -514,7 +526,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
connection->getInputChannelName());
} else if (status == status_t(FAILED_TRANSACTION)) {
LOGD("channel '%s' ~ Could not append motion sample to currently "
- "dispatchedmove event because the event has already been consumed. "
+ "dispatched move event because the event has already been consumed. "
"(Waiting for next dispatch cycle to start.)",
connection->getInputChannelName());
} else {
@@ -1253,9 +1265,37 @@ void InputDispatcher::resetKeyRepeatLocked() {
}
}
+void InputDispatcher::preemptInputDispatch() {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("preemptInputDispatch");
+#endif
+
+ bool preemptedOne = false;
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ for (size_t i = 0; i < mActiveConnections.size(); i++) {
+ Connection* connection = mActiveConnections[i];
+ if (connection->hasPendingSyncTarget()) {
+#if DEBUG_DISPATCH_CYCLE
+ LOGD("channel '%s' ~ Preempted pending synchronous dispatch",
+ connection->getInputChannelName());
+#endif
+ connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC;
+ preemptedOne = true;
+ }
+ }
+ } // release lock
+
+ if (preemptedOne) {
+ // Wake up the poll loop so it can get a head start dispatching the next event.
+ mPollLoop->wake();
+ }
+}
+
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
- LOGD("channel '%s' - Registered", inputChannel->getName().string());
+ LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string());
#endif
int receiveFd;
@@ -1288,7 +1328,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan
status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
- LOGD("channel '%s' - Unregistered", inputChannel->getName().string());
+ LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string());
#endif
int32_t receiveFd;
diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp
index 32c58b4..e1d15a4 100644
--- a/libs/ui/InputManager.cpp
+++ b/libs/ui/InputManager.cpp
@@ -85,6 +85,10 @@ int32_t InputManager::injectInputEvent(const InputEvent* event,
return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
}
+void InputManager::preemptInputDispatch() {
+ mDispatcher->preemptInputDispatch();
+}
+
void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
mReader->getCurrentInputConfiguration(outConfiguration);
}
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 1824054..8087f84 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -11,13 +11,13 @@
#define DEBUG_RAW_EVENTS 0
// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 1
+#define DEBUG_HACKS 0
// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 1
+#define DEBUG_VIRTUAL_KEYS 0
// Log debug messages about pointers.
-#define DEBUG_POINTERS 1
+#define DEBUG_POINTERS 0
// Log debug messages about pointer assignment calculations.
#define DEBUG_POINTER_ASSIGNMENT 0
@@ -630,7 +630,8 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
int32_t pressure = currentTouch.pointers[currentIndex].pressure;
if (lastTouch.idBits.hasBit(id)) {
- // Pointer still down compute average.
+ // Pointer was down before and is still down now.
+ // Compute average over history trace.
uint32_t start = averagingTouchFilter.historyStart[id];
uint32_t end = averagingTouchFilter.historyEnd[id];
@@ -644,11 +645,15 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
#endif
if (distance < AVERAGING_DISTANCE_LIMIT) {
+ // Increment end index in preparation for recording new historical data.
end += 1;
if (end > AVERAGING_HISTORY_SIZE) {
end = 0;
}
+ // If the end index has looped back to the start index then we have filled
+ // the historical trace up to the desired size so we drop the historical
+ // data at the start of the trace.
if (end == start) {
start += 1;
if (start > AVERAGING_HISTORY_SIZE) {
@@ -656,23 +661,25 @@ void InputDevice::TouchScreenState::applyAveragingTouchFilter() {
}
}
+ // Add the raw data to the historical trace.
averagingTouchFilter.historyStart[id] = start;
averagingTouchFilter.historyEnd[id] = end;
averagingTouchFilter.historyData[end].pointers[id].x = x;
averagingTouchFilter.historyData[end].pointers[id].y = y;
averagingTouchFilter.historyData[end].pointers[id].pressure = pressure;
+ // Average over all historical positions in the trace by total pressure.
int32_t averagedX = 0;
int32_t averagedY = 0;
int32_t totalPressure = 0;
for (;;) {
int32_t historicalX = averagingTouchFilter.historyData[start].pointers[id].x;
- int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].x;
+ int32_t historicalY = averagingTouchFilter.historyData[start].pointers[id].y;
int32_t historicalPressure = averagingTouchFilter.historyData[start]
.pointers[id].pressure;
- averagedX += historicalX;
- averagedY += historicalY;
+ averagedX += historicalX * historicalPressure;
+ averagedY += historicalY * historicalPressure;
totalPressure += historicalPressure;
if (start == end) {
@@ -1144,12 +1151,6 @@ void InputReader::onMultiTouchScreenStateChanged(nsecs_t when,
void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
InputDevice* device) {
- static const uint32_t POSITION_FIELDS =
- InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X
- | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y
- | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE
- | InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH;
-
/* Refresh display properties so we can map touch screen coords into display coords */
if (! refreshDisplayProperties()) {
@@ -1167,10 +1168,19 @@ void InputReader::onSingleTouchScreenStateChanged(nsecs_t when,
in->current.down = in->accumulator.btnTouch;
}
- if ((fields & POSITION_FIELDS) == POSITION_FIELDS) {
+ if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_X) {
in->current.x = in->accumulator.absX;
+ }
+
+ if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_Y) {
in->current.y = in->accumulator.absY;
+ }
+
+ if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_PRESSURE) {
in->current.pressure = in->accumulator.absPressure;
+ }
+
+ if (fields & InputDevice::SingleTouchScreenState::Accumulator::FIELD_ABS_TOOL_WIDTH) {
in->current.size = in->accumulator.absToolWidth;
}
@@ -1323,18 +1333,23 @@ bool InputReader::consumeVirtualKeyTouches(nsecs_t when,
void InputReader::dispatchVirtualKey(nsecs_t when,
InputDevice* device, uint32_t policyFlags,
int32_t keyEventAction, int32_t keyEventFlags) {
+ updateExportedVirtualKeyState();
+
int32_t keyCode = device->touchScreen.currentVirtualKey.keyCode;
int32_t scanCode = device->touchScreen.currentVirtualKey.scanCode;
nsecs_t downTime = device->touchScreen.currentVirtualKey.downTime;
int32_t metaState = globalMetaState();
- updateExportedVirtualKeyState();
-
mPolicy->virtualKeyFeedback(when, device->id, keyEventAction, keyEventFlags,
keyCode, scanCode, metaState, downTime);
- mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags,
- keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+ int32_t policyActions = mPolicy->interceptKey(when, device->id,
+ keyEventAction == KEY_EVENT_ACTION_DOWN, keyCode, scanCode, policyFlags);
+
+ if (applyStandardInputDispatchPolicyActions(when, policyActions, & policyFlags)) {
+ mDispatcher->notifyKey(when, device->id, INPUT_EVENT_NATURE_KEY, policyFlags,
+ keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime);
+ }
}
void InputReader::dispatchTouches(nsecs_t when,
@@ -1609,6 +1624,10 @@ bool InputReader::applyStandardInputDispatchPolicyActions(nsecs_t when,
*policyFlags |= POLICY_FLAG_BRIGHT_HERE;
}
+ if (policyActions & InputReaderPolicyInterface::ACTION_INTERCEPT_DISPATCH) {
+ *policyFlags |= POLICY_FLAG_INTERCEPT_DISPATCH;
+ }
+
return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH;
}
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index b2842d0..f56537a 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -430,10 +430,12 @@ status_t InputPublisher::appendMotionSample(
reinterpret_cast<char*>(mSharedMessage);
if (newBytesUsed > mAshmemSize) {
+#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory "
"buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d",
mChannel->getName().string(),
mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount);
+#endif
return NO_MEMORY;
}
@@ -444,8 +446,10 @@ status_t InputPublisher::appendMotionSample(
if (errno == EAGAIN) {
// Only possible source of contention is the consumer having consumed (or being in the
// process of consuming) the message and left the semaphore count at 0.
+#if DEBUG_TRANSPORT_ACTIONS
LOGD("channel '%s' publisher ~ Cannot append motion sample because the message has "
"already been consumed.", mChannel->getName().string());
+#endif
return FAILED_TRANSACTION;
} else {
LOGE("channel '%s' publisher ~ Error %d in sem_trywait.",
diff --git a/core/jni/android_view_InputTarget.h b/services/java/com/android/server/InputApplication.java
index 9230b1b..38420d4 100644
--- a/core/jni/android_view_InputTarget.h
+++ b/services/java/com/android/server/InputApplication.java
@@ -14,18 +14,20 @@
* limitations under the License.
*/
-#ifndef _ANDROID_VIEW_INPUTTARGET_H
-#define _ANDROID_VIEW_INPUTTARGET_H
+package com.android.server;
-#include "jni.h"
-
-namespace android {
-
-class InputTarget;
-
-extern void android_view_InputTarget_toNative(JNIEnv* env, jobject inputTargetObj,
- InputTarget* outInputTarget);
-
-} // namespace android
-
-#endif // _ANDROID_OS_INPUTTARGET_H
+/**
+ * Describes input-related application properties for use by the input dispatcher.
+ *
+ * @hide
+ */
+public final class InputApplication {
+ // Application name.
+ public String name;
+
+ // Dispatching timeout.
+ public long dispatchingTimeoutNanos;
+
+ // The application window token.
+ public Object token;
+}
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index 8d9bb29..2ba2914 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -17,23 +17,20 @@
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.pm.PackageManager;
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;
@@ -85,6 +82,10 @@ public class InputManager {
int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
private static native int nativeInjectMotionEvent(MotionEvent event, int nature,
int injectorPid, int injectorUid, boolean sync, int timeoutMillis);
+ private static native void nativeSetInputWindows(InputWindow[] windows);
+ private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
+ private static native void nativeSetFocusedApplication(InputApplication application);
+ private static native void nativePreemptInputDispatch();
// Device class as defined by EventHub.
private static final int CLASS_KEYBOARD = 0x00000001;
@@ -284,6 +285,22 @@ public class InputManager {
sync, timeoutMillis);
}
+ public void setInputWindows(InputWindow[] windows) {
+ nativeSetInputWindows(windows);
+ }
+
+ public void setFocusedApplication(InputApplication application) {
+ nativeSetFocusedApplication(application);
+ }
+
+ public void preemptInputDispatch() {
+ nativePreemptInputDispatch();
+ }
+
+ public void setInputDispatchMode(boolean enabled, boolean frozen) {
+ nativeSetInputDispatchMode(enabled, frozen);
+ }
+
public void dump(PrintWriter pw) {
// TODO
}
@@ -344,32 +361,43 @@ public class InputManager {
@SuppressWarnings("unused")
public void notifyInputChannelBroken(InputChannel inputChannel) {
- mWindowManagerService.notifyInputChannelBroken(inputChannel);
+ mWindowManagerService.mInputMonitor.notifyInputChannelBroken(inputChannel);
}
@SuppressWarnings("unused")
public long notifyInputChannelANR(InputChannel inputChannel) {
- return mWindowManagerService.notifyInputChannelANR(inputChannel);
+ return mWindowManagerService.mInputMonitor.notifyInputChannelANR(inputChannel);
}
@SuppressWarnings("unused")
public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- mWindowManagerService.notifyInputChannelRecoveredFromANR(inputChannel);
+ mWindowManagerService.mInputMonitor.notifyInputChannelRecoveredFromANR(inputChannel);
+ }
+
+ @SuppressWarnings("unused")
+ public long notifyANR(Object token) {
+ return mWindowManagerService.mInputMonitor.notifyANR(token);
}
@SuppressWarnings("unused")
- public int hackInterceptKey(int deviceId, int type, int scanCode,
+ public int interceptKeyBeforeQueueing(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);
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing(deviceId, type,
+ scanCode, keyCode, policyFlags, value, whenNanos, isScreenOn);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode,
+ int metaState, boolean down, int repeatCount, int policyFlags) {
+ return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus,
+ keyCode, metaState, down, repeatCount, policyFlags);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
+ return mContext.checkPermission(
+ android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
+ == PackageManager.PERMISSION_GRANTED;
}
@SuppressWarnings("unused")
@@ -379,15 +407,14 @@ public class InputManager {
}
@SuppressWarnings("unused")
- public void pokeUserActivityForKey(long whenNanos) {
- long when = whenNanos / 1000000;
- mPowerManagerService.userActivity(when, false,
- LocalPowerManager.BUTTON_EVENT, false);
+ public void pokeUserActivity(long eventTimeNanos, int eventType) {
+ long eventTime = eventTimeNanos / 1000000;
+ mPowerManagerService.userActivity(eventTime, false, eventType, false);
}
@SuppressWarnings("unused")
public void notifyAppSwitchComing() {
- mWindowManagerService.mKeyWaiter.appSwitchComing();
+ mWindowManagerService.mInputMonitor.notifyAppSwitchComing();
}
@SuppressWarnings("unused")
@@ -485,24 +512,5 @@ public class InputManager {
return names.toArray(new String[names.size()]);
}
-
- // TODO All code related to target identification should be moved down into native.
- @SuppressWarnings("unused")
- public int getKeyEventTargets(InputTargetList inputTargets,
- KeyEvent event, int nature, int policyFlags,
- int injectorPid, int injectorUid) {
- inputTargets.clear();
- return mWindowManagerService.getKeyEventTargetsTd(
- inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
- }
-
- @SuppressWarnings("unused")
- public int getMotionEventTargets(InputTargetList inputTargets,
- MotionEvent event, int nature, int policyFlags,
- int injectorPid, int injectorUid) {
- inputTargets.clear();
- return mWindowManagerService.getMotionEventTargetsTd(
- inputTargets, event, nature, policyFlags, injectorPid, injectorUid);
- }
}
}
diff --git a/services/java/com/android/server/InputTargetList.java b/services/java/com/android/server/InputTargetList.java
deleted file mode 100644
index 83acc8f..0000000
--- a/services/java/com/android/server/InputTargetList.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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 final 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] = null;
- }
-
- /**
- * 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] = null;
- }
-
- /**
- * Gets the input targets as a null-terminated array.
- * @return The input target array.
- */
- public InputTarget[] toNullTerminatedArray() {
- return mArray;
- }
-} \ No newline at end of file
diff --git a/services/java/com/android/server/InputWindow.java b/services/java/com/android/server/InputWindow.java
new file mode 100644
index 0000000..8da0cf1
--- /dev/null
+++ b/services/java/com/android/server/InputWindow.java
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+/**
+ * Describes input-related window properties for use by the input dispatcher.
+ *
+ * @hide
+ */
+public final class InputWindow {
+ // The input channel associated with the window.
+ public InputChannel inputChannel;
+
+ // Window layout params attributes. (WindowManager.LayoutParams)
+ public int layoutParamsFlags;
+ public int layoutParamsType;
+
+ // Dispatching timeout.
+ public long dispatchingTimeoutNanos;
+
+ // Window frame position.
+ public int frameLeft;
+ public int frameTop;
+
+ // Window touchable area.
+ public int touchableAreaLeft;
+ public int touchableAreaTop;
+ public int touchableAreaRight;
+ public int touchableAreaBottom;
+
+ // Window is visible.
+ public boolean visible;
+
+ // Window has focus.
+ public boolean hasFocus;
+
+ // Window has wallpaper. (window is the current wallpaper target)
+ public boolean hasWallpaper;
+
+ // Input event dispatching is paused.
+ public boolean paused;
+
+ // Id of process and user that owns the window.
+ public int ownerPid;
+ public int ownerUid;
+
+ public void recycle() {
+ inputChannel = null;
+ }
+}
diff --git a/services/java/com/android/server/InputWindowList.java b/services/java/com/android/server/InputWindowList.java
new file mode 100644
index 0000000..1cbb2cc
--- /dev/null
+++ b/services/java/com/android/server/InputWindowList.java
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+
+/**
+ * A specialized list of window information objects backed by an array.
+ *
+ * This class is part of an InputManager optimization to avoid allocating objects and arrays
+ * unnecessarily. Internally, it keeps an array full of demand-allocated objects that it
+ * recycles each time the list is cleared. The used portion of the array is padded with a null.
+ *
+ * The contents of the list are intended to be Z-ordered from top to bottom.
+ *
+ * @hide
+ */
+public final class InputWindowList {
+ private InputWindow[] mArray;
+ private int mCount;
+
+ /**
+ * Creates an empty list.
+ */
+ public InputWindowList() {
+ mArray = new InputWindow[8];
+ }
+
+ /**
+ * Clears the 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] = null;
+ }
+
+ /**
+ * Adds an uninitialized input window object to the list and returns it.
+ */
+ public InputWindow add() {
+ if (mCount + 1 == mArray.length) {
+ InputWindow[] oldArray = mArray;
+ mArray = new InputWindow[oldArray.length * 2];
+ System.arraycopy(oldArray, 0, mArray, 0, mCount);
+ }
+
+ // Grab object from tail (after used section) if available.
+ InputWindow item = mArray[mCount + 1];
+ if (item == null) {
+ item = new InputWindow();
+ }
+
+ mArray[mCount] = item;
+ mCount += 1;
+ mArray[mCount] = null;
+ return item;
+ }
+
+ /**
+ * Gets the input window objects as a null-terminated array.
+ * @return The input window array.
+ */
+ public InputWindow[] toNullTerminatedArray() {
+ return mArray;
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index 95ab5bc..bf86b23 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -103,7 +103,6 @@ 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;
@@ -207,6 +206,9 @@ public class WindowManagerService extends IWindowManager.Stub
// Maximum number of milliseconds to wait for input event injection.
// FIXME is this value reasonable?
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
+
+ // Default input dispatching timeout in nanoseconds.
+ private static final long DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS = 5000 * 1000000L;
static final int INJECT_FAILED = 0;
static final int INJECT_SUCCEEDED = 1;
@@ -2061,8 +2063,8 @@ public class WindowManagerService extends IWindowManager.Stub
boolean focusChanged = false;
if (win.canReceiveKeys()) {
- if ((focusChanged=updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS))
- == true) {
+ focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS);
+ if (focusChanged) {
imMayMove = false;
}
}
@@ -2078,10 +2080,9 @@ public class WindowManagerService extends IWindowManager.Stub
//dump();
if (focusChanged) {
- if (mCurrentFocus != null) {
- mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
- }
+ finishUpdateFocusedWindowAfterAssignLayersLocked();
}
+
if (localLOGV) Slog.v(
TAG, "New client " + client.asBinder()
+ ": window=" + win);
@@ -2183,10 +2184,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
private void removeWindowInnerLocked(Session session, WindowState win) {
- mKeyWaiter.finishedKey(session, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releasePendingPointerLocked(win.mSession);
- mKeyWaiter.releasePendingTrackballLocked(win.mSession);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBeingRemovedLw(win);
+ } else {
+ mKeyWaiter.finishedKey(session, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ mKeyWaiter.releasePendingPointerLocked(win.mSession);
+ mKeyWaiter.releasePendingTrackballLocked(win.mSession);
+ }
win.mRemoved = true;
@@ -2554,8 +2559,12 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win, transit, false)) {
focusMayChange = true;
win.mExiting = true;
- mKeyWaiter.finishedKey(session, client, true,
- KeyWaiter.RETURN_NOTHING);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(win);
+ } else {
+ mKeyWaiter.finishedKey(session, client, true,
+ KeyWaiter.RETURN_NOTHING);
+ }
} else if (win.isAnimating()) {
// Currently in a hide animation... turn this into
// an exit.
@@ -3016,8 +3025,12 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.isVisibleNow()) {
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBeingRemovedLw(win);
+ } else {
+ mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ }
changed = true;
}
}
@@ -3048,6 +3061,20 @@ public class WindowManagerService extends IWindowManager.Stub
"addAppToken()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
+
+ // Get the dispatching timeout here while we are not holding any locks so that it
+ // can be cached by the AppWindowToken. The timeout value is used later by the
+ // input dispatcher in code that does hold locks. If we did not cache the value
+ // here we would run the chance of introducing a deadlock between the window manager
+ // (which holds locks while updating the input dispatcher state) and the activity manager
+ // (which holds locks while querying the application token).
+ long inputDispatchingTimeoutNanos;
+ try {
+ inputDispatchingTimeoutNanos = token.getKeyDispatchingTimeout() * 1000000L;
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Could not get dispatching timeout.", ex);
+ inputDispatchingTimeoutNanos = DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3056,6 +3083,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
wtoken = new AppWindowToken(token);
+ wtoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
wtoken.groupId = groupId;
wtoken.appFullscreen = fullscreen;
wtoken.requestedOrientation = requestedOrientation;
@@ -3319,7 +3347,13 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.v(TAG, "Clearing focused app, was " + mFocusedApp);
changed = mFocusedApp != null;
mFocusedApp = null;
- mKeyWaiter.tickle();
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(null);
+ }
+ } else {
+ mKeyWaiter.tickle();
+ }
} else {
AppWindowToken newFocus = findAppWindowToken(token);
if (newFocus == null) {
@@ -3329,7 +3363,13 @@ public class WindowManagerService extends IWindowManager.Stub
changed = mFocusedApp != newFocus;
mFocusedApp = newFocus;
if (DEBUG_FOCUS) Slog.v(TAG, "Set focused app to: " + mFocusedApp);
- mKeyWaiter.tickle();
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ if (changed) {
+ mInputMonitor.setFocusedAppLw(newFocus);
+ }
+ } else {
+ mKeyWaiter.tickle();
+ }
}
if (moveFocusNow && changed) {
@@ -3640,8 +3680,12 @@ public class WindowManagerService extends IWindowManager.Stub
applyAnimationLocked(win,
WindowManagerPolicy.TRANSIT_EXIT, false);
}
- mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
- KeyWaiter.RETURN_NOTHING);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(win);
+ } else {
+ mKeyWaiter.finishedKey(win.mSession, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ }
changed = true;
}
}
@@ -3926,7 +3970,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FOCUS) Slog.v(TAG, "Removing focused app token:" + wtoken);
mFocusedApp = null;
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
- mKeyWaiter.tickle();
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.setFocusedAppLw(null);
+ } else {
+ mKeyWaiter.tickle();
+ }
}
} else {
Slog.w(TAG, "Attempted to remove non-existing app token: " + token);
@@ -5075,456 +5123,375 @@ public class WindowManagerService extends IWindowManager.Stub
return true;
}
- /* Notifies the window manager about a broken input channel.
- *
- * Called by the InputManager.
- */
- public void notifyInputChannelBroken(InputChannel inputChannel) {
- synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
- if (windowState == null) {
- return; // irrelevant
- }
-
- Slog.i(TAG, "WINDOW DIED " + windowState);
- removeWindowLocked(windowState.mSession, windowState);
- }
- }
-
- /* Notifies the window manager about a broken input channel.
- *
- * Called by the InputManager.
- */
- public long notifyInputChannelANR(InputChannel inputChannel) {
- IApplicationToken appToken;
- synchronized (mWindowMap) {
- WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
- if (windowState == null) {
- return -2; // irrelevant, abort dispatching (-2)
- }
-
- Slog.i(TAG, "Input event dispatching timed out sending to "
- + windowState.mAttrs.getTitle());
- appToken = windowState.getAppToken();
- }
-
- try {
- // Notify the activity manager about the timeout and let it decide whether
- // to abort dispatching or keep waiting.
- boolean abort = appToken.keyDispatchingTimedOut();
- if (abort) {
- return -2; // abort dispatching
- }
-
- // Return new timeout.
- // We use -1 for infinite timeout to avoid clash with -2 magic number.
- long newTimeout = appToken.getKeyDispatchingTimeout() * 1000000;
- return newTimeout < 0 ? -1 : newTimeout;
- } catch (RemoteException ex) {
- return -2; // abort dispatching
- }
- }
-
- /* Notifies the window manager about a broken input channel.
- *
- * Called by the InputManager.
- */
- public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
- // Nothing to do just now.
- // Just wait for the user to dismiss the ANR dialog.
-
- // TODO We could try to automatically dismiss the ANR dialog on recovery
- // although that might be disorienting.
- }
-
- private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
- int windowCount = mWindows.size();
- for (int i = 0; i < windowCount; i++) {
- WindowState windowState = (WindowState) mWindows.get(i);
- if (windowState.mInputChannel == inputChannel) {
- return windowState;
- }
- }
-
- return null;
- }
-
// -------------------------------------------------------------
// Input Events and Focus Management
// -------------------------------------------------------------
- private boolean checkInjectionPermissionTd(WindowState focus,
- int injectorPid, int injectorUid) {
- if (injectorUid > 0 && (focus == null || injectorUid != focus.mSession.mUid)) {
- if (mContext.checkPermission(
- android.Manifest.permission.INJECT_EVENTS, injectorPid, injectorUid)
- != PackageManager.PERMISSION_GRANTED) {
- Slog.w(TAG, "Permission denied: injecting key event from pid "
- + injectorPid + " uid " + injectorUid + " to window " + focus
- + " owned by uid " + focus.mSession.mUid);
- return false;
- }
- }
- return true;
- }
+ InputMonitor mInputMonitor = new InputMonitor();
- /* Gets the input targets for a key event.
- *
- * Called by the InputManager on the InputDispatcher thread.
- */
- public int getKeyEventTargetsTd(InputTargetList inputTargets,
- KeyEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
- 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?
+ /* Tracks the progress of input dispatch and ensures that input dispatch state
+ * is kept in sync with changes in window focus, visibility, registration, and
+ * other relevant Window Manager state transitions. */
+ final class InputMonitor {
+ // Current window with input focus for keys and other non-touch events. May be null.
+ private WindowState mInputFocus;
- WindowState focus = getFocusedWindow();
+ // When true, prevents input dispatch from proceeding until set to false again.
+ private boolean mInputDispatchFrozen;
- if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
- return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- }
+ // When true, input dispatch proceeds normally. Otherwise all events are dropped.
+ private boolean mInputDispatchEnabled = true;
+
+ // Temporary list of windows information to provide to the input dispatcher.
+ private InputWindowList mTempInputWindows = new InputWindowList();
- if (mPolicy.interceptKeyTi(focus, event.getKeyCode(), event.getMetaState(),
- event.getAction() == KeyEvent.ACTION_DOWN,
- event.getRepeatCount(), event.getFlags())) {
- // Policy consumed the event.
- return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
- }
+ // Temporary input application object to provide to the input dispatcher.
+ private InputApplication mTempInputApplication = new InputApplication();
- if (focus == null) {
- return InputManager.INPUT_EVENT_INJECTION_FAILED;
+ /* Notifies the window manager about a broken input channel.
+ *
+ * Called by the InputManager.
+ */
+ public void notifyInputChannelBroken(InputChannel inputChannel) {
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState == null) {
+ return; // irrelevant
+ }
+
+ Slog.i(TAG, "WINDOW DIED " + windowState);
+ removeWindowLocked(windowState.mSession, windowState);
+ }
}
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-
- addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
- return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
- }
-
- /* Gets the input targets for a motion event.
- *
- * Called by the InputManager on the InputDispatcher thread.
- */
- public int getMotionEventTargetsTd(InputTargetList inputTargets,
- MotionEvent event, int nature, int policyFlags, int injectorPid, int injectorUid) {
- switch (nature) {
- case InputQueue.INPUT_EVENT_NATURE_TRACKBALL:
- return getMotionEventTargetsForTrackballTd(inputTargets, event, policyFlags,
- injectorPid, injectorUid);
-
- case InputQueue.INPUT_EVENT_NATURE_TOUCH:
- return getMotionEventTargetsForTouchTd(inputTargets, event, policyFlags,
- injectorPid, injectorUid);
+ /* Notifies the window manager about an input channel that is not responding.
+ * The method can either cause dispatching to be aborted by returning -2 or
+ * return a new timeout in nanoseconds.
+ *
+ * Called by the InputManager.
+ */
+ public long notifyInputChannelANR(InputChannel inputChannel) {
+ AppWindowToken token;
+ synchronized (mWindowMap) {
+ WindowState windowState = getWindowStateForInputChannelLocked(inputChannel);
+ if (windowState == null) {
+ return -2; // irrelevant, abort dispatching (-2)
+ }
- default:
- return InputManager.INPUT_EVENT_INJECTION_FAILED;
+ Slog.i(TAG, "Input event dispatching timed out sending to "
+ + windowState.mAttrs.getTitle());
+ token = windowState.mAppToken;
+ }
+
+ return notifyANRInternal(token);
}
- }
- /* Gets the input targets for a trackball event.
- *
- * Called by the InputManager on the InputDispatcher thread.
- */
- private int getMotionEventTargetsForTrackballTd(InputTargetList inputTargets,
- MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
- WindowState focus = getFocusedWindow();
-
- if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
- return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- }
-
- if (focus == null) {
- return InputManager.INPUT_EVENT_INJECTION_FAILED;
+ /* Notifies the window manager about an input channel spontaneously recovering from ANR
+ * by successfully delivering the event that originally timed out.
+ *
+ * Called by the InputManager.
+ */
+ public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
+ // Nothing to do just now.
+ // Just wait for the user to dismiss the ANR dialog.
}
- wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
-
- addInputTargetTd(inputTargets, focus, InputTarget.FLAG_SYNC);
- return InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
- }
-
- /* Set to true when a fat touch has been detected during the processing of a touch event.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Set to true whenever a fat touch is detected and reset to false on ACTION_UP.
- */
- private boolean mFatTouch;
-
- /* Set to true when we think the touch event.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Set to true on ACTION_DOWN and set to false on ACTION_UP.
- */
- private boolean mTouchDown;
-
- /* Current target of Motion events.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Initialized on ACTION_DOWN and cleared on ACTION_UP.
- */
- private WindowState mTouchFocus;
+ /* Notifies the window manager about an application that is not responding
+ * in general rather than with respect to a particular input channel.
+ * The method can either cause dispatching to be aborted by returning -2 or
+ * return a new timeout in nanoseconds.
+ *
+ * Called by the InputManager.
+ */
+ public long notifyANR(Object token) {
+ AppWindowToken appWindowToken = (AppWindowToken) token;
- /* Windows above the target that would like to receive an "outside" touch event
- * for any down events outside of them.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Initialized on ACTION_DOWN and cleared immediately afterwards.
- */
- private ArrayList<WindowState> mOutsideTouchTargets = new ArrayList<WindowState>();
-
- /* Wallpaper windows that are currently receiving touch events.
- *
- * Only used by getMotionEventTargetsForTouchTd.
- * Initialized on ACTION_DOWN and cleared on ACTION_UP.
- */
- private ArrayList<WindowState> mWallpaperTouchTargets = new ArrayList<WindowState>();
-
- /* Gets the input targets for a touch event.
- *
- * Called by the InputManager on the InputDispatcher thread.
- */
- private int getMotionEventTargetsForTouchTd(InputTargetList inputTargets,
- MotionEvent event, int policyFlags, int injectorPid, int injectorUid) {
- final int action = event.getAction();
-
- if (action == MotionEvent.ACTION_DOWN) {
- updateTouchFocusBeforeDownTd(event, policyFlags);
- } else {
- updateTouchFocusBeforeNonDownTd(event, policyFlags);
+ Slog.i(TAG, "Input event dispatching timed out sending to application "
+ + appWindowToken.stringName);
+ return notifyANRInternal(appWindowToken);
}
-
- boolean skipDelivery = false;
- int touchTargetFlags = 0;
- int injectionResult = InputManager.INPUT_EVENT_INJECTION_SUCCEEDED;
- WindowState focusedTouchTarget = mTouchFocus;
- if (focusedTouchTarget == 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);
- injectionResult = InputManager.INPUT_EVENT_INJECTION_FAILED;
- }
- } else {
- // We have a valid focused touch target.
- if (! checkInjectionPermissionTd(focusedTouchTarget, injectorPid, injectorUid)) {
- return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
- }
-
- wakeupIfNeeded(focusedTouchTarget, eventType(event));
-
- if ((focusedTouchTarget.mAttrs.flags &
- WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
- // Target wants to ignore fat touch events
- boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
-
- if (cheekPress) {
- if ((action == MotionEvent.ACTION_DOWN)) {
- mFatTouch = true;
- skipDelivery = true;
- } else {
- if (! mFatTouch) {
- // cancel the earlier event
- touchTargetFlags |= InputTarget.FLAG_CANCEL;
- mFatTouch = true;
- } else {
- skipDelivery = true;
- }
+ private long notifyANRInternal(AppWindowToken token) {
+ if (token != null && token.appToken != null) {
+ try {
+ // Notify the activity manager about the timeout and let it decide whether
+ // to abort dispatching or keep waiting.
+ boolean abort = token.appToken.keyDispatchingTimedOut();
+ if (! abort) {
+ // The activity manager declined to abort dispatching.
+ // Wait a bit longer and timeout again later.
+ return token.inputDispatchingTimeoutNanos;
}
+ } catch (RemoteException ex) {
}
}
+ return -2; // abort dispatching
}
- if (! skipDelivery) {
- int outsideTargetCount = mOutsideTouchTargets.size();
- for (int i = 0; i < outsideTargetCount; i++) {
- WindowState outsideTouchTarget = mOutsideTouchTargets.get(i);
- addInputTargetTd(inputTargets, outsideTouchTarget,
- InputTarget.FLAG_OUTSIDE | touchTargetFlags);
- }
-
- int wallpaperTargetCount = mWallpaperTouchTargets.size();
- for (int i = 0; i < wallpaperTargetCount; i++) {
- WindowState wallpaperTouchTarget = mWallpaperTouchTargets.get(i);
- addInputTargetTd(inputTargets, wallpaperTouchTarget,
- touchTargetFlags);
- }
-
- if (focusedTouchTarget != null) {
- addInputTargetTd(inputTargets, focusedTouchTarget,
- InputTarget.FLAG_SYNC | touchTargetFlags);
+ private WindowState getWindowStateForInputChannel(InputChannel inputChannel) {
+ synchronized (mWindowMap) {
+ return getWindowStateForInputChannelLocked(inputChannel);
}
}
-
- if (action == MotionEvent.ACTION_UP) {
- updateTouchFocusAfterUpTd(event, policyFlags);
- }
- return injectionResult;
- }
-
- private void updateTouchFocusBeforeDownTd(MotionEvent event, int policyFlags) {
- if (mTouchDown) {
- // This is weird, we got a 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);
- updateTouchFocusAfterUpTd(event, policyFlags);
+ private WindowState getWindowStateForInputChannelLocked(InputChannel inputChannel) {
+ int windowCount = mWindows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState windowState = (WindowState) mWindows.get(i);
+ if (windowState.mInputChannel == inputChannel) {
+ return windowState;
+ }
+ }
+
+ return null;
}
- mTouchDown = true;
- mPowerManager.logPointerDownEvent();
-
- final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
- synchronized (mWindowMap) {
- final int x = (int) event.getX();
- final int y = (int) event.getY();
-
+ /* Updates the cached window information provided to the input dispatcher. */
+ public void updateInputWindowsLw() {
+ // Populate the input window list with information about all of the windows that
+ // could potentially receive input.
+ // As an optimization, we could try to prune the list of windows but this turns
+ // out to be difficult because only the native code knows for sure which window
+ // currently has touch focus.
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);
+ for (int i = N - 1; i >= 0; i--) {
+ final WindowState child = (WindowState) windows.get(i);
+ if (child.mInputChannel == null) {
+ // Skip this window because it cannot possibly receive input.
+ continue;
+ }
final int flags = child.mAttrs.flags;
- if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
- if (topErrWindow == null) {
- topErrWindow = child;
- }
- }
+ final int type = child.mAttrs.type;
- if (!child.isVisibleLw()) {
- //Slog.i(TAG, "Not visible!");
- continue;
- }
+ final boolean hasFocus = (child == mInputFocus);
+ final boolean isVisible = child.isVisibleLw();
+ final boolean hasWallpaper = (child == mWallpaperTarget)
+ && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
- 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;
- }
+ // Add a window to our list of input windows.
+ final InputWindow inputWindow = mTempInputWindows.add();
+ inputWindow.inputChannel = child.mInputChannel;
+ inputWindow.layoutParamsFlags = flags;
+ inputWindow.layoutParamsType = type;
+ inputWindow.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
+ inputWindow.visible = isVisible;
+ inputWindow.hasFocus = hasFocus;
+ inputWindow.hasWallpaper = hasWallpaper;
+ inputWindow.paused = child.mAppToken != null ? child.mAppToken.paused : false;
+ inputWindow.ownerPid = child.mSession.mPid;
+ inputWindow.ownerUid = child.mSession.mUid;
+
+ final Rect frame = child.mFrame;
+ inputWindow.frameLeft = frame.left;
+ inputWindow.frameTop = frame.top;
+
+ switch (child.mTouchableInsets) {
+ default:
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME:
+ inputWindow.touchableAreaLeft = frame.left;
+ inputWindow.touchableAreaTop = frame.top;
+ inputWindow.touchableAreaRight = frame.right;
+ inputWindow.touchableAreaBottom = frame.bottom;
+ break;
+
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: {
+ Rect inset = child.mGivenContentInsets;
+ inputWindow.touchableAreaLeft = frame.left + inset.left;
+ inputWindow.touchableAreaTop = frame.top + inset.top;
+ inputWindow.touchableAreaRight = frame.right - inset.right;
+ inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
+ break;
+ }
+
+ case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: {
+ Rect inset = child.mGivenVisibleInsets;
+ inputWindow.touchableAreaLeft = frame.left + inset.left;
+ inputWindow.touchableAreaTop = frame.top + inset.top;
+ inputWindow.touchableAreaRight = frame.right - inset.right;
+ inputWindow.touchableAreaBottom = frame.bottom - inset.bottom;
break;
}
- }
-
- if ((flags & WindowManager.LayoutParams
- .FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
- //Slog.i(TAG, "Adding to outside target list: " + child);
- mOutsideTouchTargets.add(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;
- }
+ // Send windows to native code.
+ mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray());
- // Drop the touch focus if the window is not visible.
- if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
- mTouchFocus = null;
- }
+ // Clear the list in preparation for the next round.
+ // Also avoids keeping InputChannel objects referenced unnecessarily.
+ mTempInputWindows.clear();
+ }
+
+ /* Notifies that an app switch key (BACK / HOME) has just been pressed.
+ * This essentially starts a .5 second timeout for the application to process
+ * subsequent input events while waiting for the app switch to occur. If it takes longer
+ * than this, the pending events will be dropped.
+ */
+ public void notifyAppSwitchComing() {
+ // TODO Not implemented yet. Should go in the native side.
+ }
+
+ /* Provides an opportunity for the window manager policy to intercept early key
+ * processing as soon as the key has been read from the device. */
+ public int interceptKeyBeforeQueueing(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;
- // Determine wallpaper targets.
- if (mTouchFocus != null
- && mTouchFocus == mWallpaperTarget
- && mTouchFocus.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) {
- mWallpaperTouchTargets.add(wallpaper);
+ return mPolicy.interceptKeyTq(event, isScreenOn);
+ }
+
+ /* Provides an opportunity for the window manager policy to process a key before
+ * ordinary dispatch. */
+ public boolean interceptKeyBeforeDispatching(InputChannel focus, int keyCode,
+ int metaState, boolean down, int repeatCount, int policyFlags) {
+ WindowState windowState = getWindowStateForInputChannel(focus);
+ return mPolicy.interceptKeyTi(windowState, keyCode, metaState, down, repeatCount,
+ policyFlags);
+ }
+
+ /* Called when the current input focus changes.
+ * Layer assignment is assumed to be complete by the time this is called.
+ */
+ public void setInputFocusLw(WindowState newWindow) {
+ if (DEBUG_INPUT) {
+ Slog.d(TAG, "Input focus has changed to " + newWindow);
+ }
+
+ if (newWindow != mInputFocus) {
+ if (newWindow != null && newWindow.canReceiveKeys()) {
+ // If the new input focus is an error window or appears above the current
+ // input focus, preempt any pending synchronous dispatch so that we can
+ // start delivering events to the new input focus as soon as possible.
+ if ((newWindow.mAttrs.flags & FLAG_SYSTEM_ERROR) != 0) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "New SYSTEM_ERROR window; resetting state");
}
+ preemptInputDispatchLw();
+ } else if (mInputFocus != null && newWindow.mLayer > mInputFocus.mLayer) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Transferring focus to new window at higher layer: "
+ + "old win layer=" + mInputFocus.mLayer
+ + ", new win layer=" + newWindow.mLayer);
+ }
+ preemptInputDispatchLw();
}
+
+ // Displaying a window implicitly causes dispatching to be unpaused.
+ // This is to protect against bugs if someone pauses dispatching but
+ // forgets to resume.
+ newWindow.mToken.paused = false;
}
+
+ mInputFocus = newWindow;
+ updateInputWindowsLw();
}
}
- }
-
- private void updateTouchFocusBeforeNonDownTd(MotionEvent event, int policyFlags) {
- synchronized (mWindowMap) {
- // Drop the touch focus if the window is not visible.
- if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
- mTouchFocus = null;
- mWallpaperTouchTargets.clear();
+
+ public void windowIsBecomingInvisibleLw(WindowState window) {
+ // The window is becoming invisible. Preempt input dispatch in progress
+ // so that the next window below can receive focus.
+ if (window == mInputFocus) {
+ mInputFocus = null;
+ preemptInputDispatchLw();
}
+
+ updateInputWindowsLw();
}
- }
-
- private void updateTouchFocusAfterUpTd(MotionEvent event, int policyFlags) {
- mFatTouch = false;
- mTouchDown = false;
- mTouchFocus = null;
- mOutsideTouchTargets.clear();
- mWallpaperTouchTargets.clear();
- mPowerManager.logPointerUpEvent();
- }
-
- /* Adds a window to a list of input targets.
- * Do NOT call this method while holding any locks because the call to
- * appToken.getKeyDispatchingTimeout() can potentially call into the ActivityManager
- * and create a deadlock hazard.
- */
- private void addInputTargetTd(InputTargetList inputTargets, WindowState window, int flags) {
- if (window.mInputChannel == null) {
- return;
+ /* Tells the dispatcher to stop waiting for its current synchronous event targets.
+ * Essentially, just makes those dispatches asynchronous so a new dispatch cycle
+ * can begin.
+ */
+ private void preemptInputDispatchLw() {
+ mInputManager.preemptInputDispatch();
}
- 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);
+ public void setFocusedAppLw(AppWindowToken newApp) {
+ // Focused app has changed.
+ if (newApp == null) {
+ mInputManager.setFocusedApplication(null);
+ } else {
+ mTempInputApplication.name = newApp.toString();
+ mTempInputApplication.dispatchingTimeoutNanos =
+ newApp.inputDispatchingTimeoutNanos;
+ mTempInputApplication.token = newApp;
+
+ mInputManager.setFocusedApplication(mTempInputApplication);
+ }
+ }
+
+ public void windowIsBeingRemovedLw(WindowState window) {
+ // Window is being removed.
+ updateInputWindowsLw();
+ }
+
+ public void pauseDispatchingLw(WindowToken window) {
+ if (! window.paused) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Pausing WindowToken " + window);
+ }
+
+ window.paused = true;
+ updateInputWindowsLw();
+ }
+ }
+
+ public void resumeDispatchingLw(WindowToken window) {
+ if (window.paused) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Resuming WindowToken " + window);
+ }
+
+ window.paused = false;
+ updateInputWindowsLw();
}
}
- inputTargets.add(window.mInputChannel, flags, timeoutNanos,
- - window.mFrame.left, - window.mFrame.top);
+ public void freezeInputDispatchingLw() {
+ if (! mInputDispatchFrozen) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Freezing input dispatching");
+ }
+
+ mInputDispatchFrozen = true;
+ updateInputDispatchModeLw();
+ }
+ }
+
+ public void thawInputDispatchingLw() {
+ if (mInputDispatchFrozen) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Thawing input dispatching");
+ }
+
+ mInputDispatchFrozen = false;
+ updateInputDispatchModeLw();
+ }
+ }
+
+ public void setEventDispatchingLw(boolean enabled) {
+ if (mInputDispatchEnabled != enabled) {
+ if (DEBUG_INPUT) {
+ Slog.v(TAG, "Setting event dispatching to " + enabled);
+ }
+
+ mInputDispatchEnabled = enabled;
+ updateInputDispatchModeLw();
+ }
+ }
+
+ private void updateInputDispatchModeLw() {
+ mInputManager.setInputDispatchMode(mInputDispatchEnabled, mInputDispatchFrozen);
+ }
}
private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
@@ -5582,6 +5549,8 @@ public class WindowManagerService extends IWindowManager.Stub
return OTHER_EVENT;
}
}
+
+ private boolean mFatTouch; // remove me together with dispatchPointer
/**
* @return Returns true if event was dispatched, false if it was dropped for any reason
@@ -5978,7 +5947,11 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- mKeyWaiter.pauseDispatchingLocked(token);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.pauseDispatchingLw(token);
+ } else {
+ mKeyWaiter.pauseDispatchingLocked(token);
+ }
}
}
}
@@ -5992,7 +5965,11 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
WindowToken token = mTokenMap.get(_token);
if (token != null) {
- mKeyWaiter.resumeDispatchingLocked(token);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.resumeDispatchingLw(token);
+ } else {
+ mKeyWaiter.resumeDispatchingLocked(token);
+ }
}
}
}
@@ -6004,7 +5981,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized (mWindowMap) {
- mKeyWaiter.setEventDispatchingLocked(enabled);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.setEventDispatchingLw(enabled);
+ } else {
+ mKeyWaiter.setEventDispatchingLocked(enabled);
+ }
}
}
@@ -7383,6 +7364,9 @@ public class WindowManagerService extends IWindowManager.Stub
public void finishKey(IWindow window) {
if (localLOGV) Slog.v(
TAG, "IWindow finishKey called for " + window);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be called anymore.");
+ }
mKeyWaiter.finishedKey(this, window, false,
KeyWaiter.RETURN_NOTHING);
}
@@ -7390,6 +7374,9 @@ public class WindowManagerService extends IWindowManager.Stub
public MotionEvent getPendingPointerMove(IWindow window) {
if (localLOGV) Slog.v(
TAG, "IWindow getPendingMotionEvent called for " + window);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be called anymore.");
+ }
return mKeyWaiter.finishedKey(this, window, false,
KeyWaiter.RETURN_PENDING_POINTER);
}
@@ -7397,6 +7384,9 @@ public class WindowManagerService extends IWindowManager.Stub
public MotionEvent getPendingTrackballMove(IWindow window) {
if (localLOGV) Slog.v(
TAG, "IWindow getPendingMotionEvent called for " + window);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ throw new IllegalStateException("Should not be called anymore.");
+ }
return mKeyWaiter.finishedKey(this, window, false,
KeyWaiter.RETURN_PENDING_TRACKBALL);
}
@@ -7943,6 +7933,12 @@ public class WindowManagerService extends IWindowManager.Stub
public IApplicationToken getAppToken() {
return mAppToken != null ? mAppToken.appToken : null;
}
+
+ public long getInputDispatchingTimeoutNanos() {
+ return mAppToken != null
+ ? mAppToken.inputDispatchingTimeoutNanos
+ : DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+ }
public boolean hasAppShownWindows() {
return mAppToken != null ? mAppToken.firstWindowDrawn : false;
@@ -8079,10 +8075,12 @@ public class WindowManagerService extends IWindowManager.Stub
// Window is no longer on-screen, so can no longer receive
// key events... if we were waiting for it to finish
// handling a key event, the wait is over!
- mKeyWaiter.finishedKey(mSession, mClient, true,
- KeyWaiter.RETURN_NOTHING);
- mKeyWaiter.releasePendingPointerLocked(mSession);
- mKeyWaiter.releasePendingTrackballLocked(mSession);
+ if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+ mKeyWaiter.finishedKey(mSession, mClient, true,
+ KeyWaiter.RETURN_NOTHING);
+ mKeyWaiter.releasePendingPointerLocked(mSession);
+ mKeyWaiter.releasePendingTrackballLocked(mSession);
+ }
if (mAppToken != null && this == mAppToken.startingWindow) {
mAppToken.startingDisplayed = false;
@@ -8098,6 +8096,10 @@ public class WindowManagerService extends IWindowManager.Stub
i--;
WindowState c = (WindowState)mChildWindows.get(i);
c.mAttachedHidden = true;
+
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(c);
+ }
}
if (mReportDestroySurface) {
@@ -8405,7 +8407,14 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Error hiding surface in " + this, e);
}
mLastHidden = true;
- mKeyWaiter.releasePendingPointerLocked(mSession);
+
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ for (int i=0; i<N; i++) {
+ mInputMonitor.windowIsBecomingInvisibleLw((WindowState)mChildWindows.get(i));
+ }
+ } else {
+ mKeyWaiter.releasePendingPointerLocked(mSession);
+ }
}
mExiting = false;
if (mRemoveOnExit) {
@@ -9098,6 +9107,9 @@ public class WindowManagerService extends IWindowManager.Stub
int groupId = -1;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ // The input dispatching timeout for this application token in nanoseconds.
+ long inputDispatchingTimeoutNanos;
// These are used for determining when all windows associated with
// an activity have been drawn, so they can be made visible together
@@ -10158,6 +10170,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
}
+
+ // Window frames may have changed. Tell the input dispatcher about it.
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.updateInputWindowsLw();
+ }
return mPolicy.finishLayoutLw();
}
@@ -10958,7 +10975,11 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.w(TAG, "Exception hiding surface in " + w);
}
}
- mKeyWaiter.releasePendingPointerLocked(w.mSession);
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.windowIsBecomingInvisibleLw(w);
+ } else {
+ mKeyWaiter.releasePendingPointerLocked(w.mSession);
+ }
}
// If we are waiting for this window to handle an
// orientation change, well, it is hidden, so
@@ -11548,14 +11569,26 @@ public class WindowManagerService extends IWindowManager.Stub
assignLayersLocked();
}
}
-
- if (newFocus != null && mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
- mKeyWaiter.handleNewWindowLocked(newFocus);
+
+ if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
+ // If we defer assigning layers, then the caller is responsible for
+ // doing this part.
+ finishUpdateFocusedWindowAfterAssignLayersLocked();
}
return true;
}
return false;
}
+
+ private void finishUpdateFocusedWindowAfterAssignLayersLocked() {
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.setInputFocusLw(mCurrentFocus);
+ } else {
+ if (mCurrentFocus != null) {
+ mKeyWaiter.handleNewWindowLocked(mCurrentFocus);
+ }
+ }
+ }
private WindowState computeFocusedWindowLocked() {
WindowState result = null;
@@ -11634,8 +11667,10 @@ public class WindowManagerService extends IWindowManager.Stub
// still frozen, the events will continue to be blocked while the
// successive orientation change is processed. To prevent spurious
// ANRs, we reset the event dispatch timeout in this case.
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
+ if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+ synchronized (mKeyWaiter) {
+ mKeyWaiter.mWasFrozen = true;
+ }
}
return;
}
@@ -11658,6 +11693,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_FREEZE) Slog.v(TAG, "*** FREEZING DISPLAY", new RuntimeException());
mDisplayFrozen = true;
+
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.freezeInputDispatchingLw();
+ }
+
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
mNextAppTransitionPackage = null;
@@ -11691,9 +11731,13 @@ public class WindowManagerService extends IWindowManager.Stub
// Reset the key delivery timeout on unfreeze, too. We force a wakeup here
// too because regular key delivery processing should resume immediately.
- synchronized (mKeyWaiter) {
- mKeyWaiter.mWasFrozen = true;
- mKeyWaiter.notifyAll();
+ if (ENABLE_NATIVE_INPUT_DISPATCH) {
+ mInputMonitor.thawInputDispatchingLw();
+ } else {
+ synchronized (mKeyWaiter) {
+ mKeyWaiter.mWasFrozen = true;
+ mKeyWaiter.notifyAll();
+ }
}
// While the display is frozen we don't re-compute the orientation
@@ -11949,20 +11993,25 @@ public class WindowManagerService extends IWindowManager.Stub
}
pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
- pw.println(" KeyWaiter state:");
- pw.print(" mLastWin="); pw.print(mKeyWaiter.mLastWin);
- pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
- pw.print(" mFinished="); pw.print(mKeyWaiter.mFinished);
- pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
- pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
- pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
+
+ if (! ENABLE_NATIVE_INPUT_DISPATCH) {
+ pw.println(" KeyWaiter state:");
+ pw.print(" mLastWin="); pw.print(mKeyWaiter.mLastWin);
+ pw.print(" mLastBinder="); pw.println(mKeyWaiter.mLastBinder);
+ pw.print(" mFinished="); pw.print(mKeyWaiter.mFinished);
+ pw.print(" mGotFirstWindow="); pw.print(mKeyWaiter.mGotFirstWindow);
+ pw.print(" mEventDispatching="); pw.print(mKeyWaiter.mEventDispatching);
+ pw.print(" mTimeToSwitch="); pw.println(mKeyWaiter.mTimeToSwitch);
+ }
}
}
+ // Called by the heartbeat to ensure locks are not held indefnitely (for deadlock detection).
public void monitor() {
synchronized (mWindowMap) { }
synchronized (mKeyguardTokenWatcher) { }
synchronized (mKeyWaiter) { }
+ synchronized (mInputMonitor) { }
}
public void virtualKeyFeedback(KeyEvent event) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 1a6119a..0106e6c 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -19,14 +19,17 @@
//#define LOG_NDEBUG 0
// Log debug messages about InputReaderPolicy
-#define DEBUG_INPUT_READER_POLICY 1
+#define DEBUG_INPUT_READER_POLICY 0
// Log debug messages about InputDispatcherPolicy
-#define DEBUG_INPUT_DISPATCHER_POLICY 1
+#define DEBUG_INPUT_DISPATCHER_POLICY 0
+// Log debug messages about input focus tracking
+#define DEBUG_FOCUS 0
#include "JNIHelp.h"
#include "jni.h"
+#include <limits.h>
#include <android_runtime/AndroidRuntime.h>
#include <ui/InputReader.h>
#include <ui/InputDispatcher.h>
@@ -37,10 +40,94 @@
#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 {
+// Window flags from WindowManager.LayoutParams
+enum {
+ FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001,
+ FLAG_DIM_BEHIND = 0x00000002,
+ FLAG_BLUR_BEHIND = 0x00000004,
+ FLAG_NOT_FOCUSABLE = 0x00000008,
+ FLAG_NOT_TOUCHABLE = 0x00000010,
+ FLAG_NOT_TOUCH_MODAL = 0x00000020,
+ FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040,
+ FLAG_KEEP_SCREEN_ON = 0x00000080,
+ FLAG_LAYOUT_IN_SCREEN = 0x00000100,
+ FLAG_LAYOUT_NO_LIMITS = 0x00000200,
+ FLAG_FULLSCREEN = 0x00000400,
+ FLAG_FORCE_NOT_FULLSCREEN = 0x00000800,
+ FLAG_DITHER = 0x00001000,
+ FLAG_SECURE = 0x00002000,
+ FLAG_SCALED = 0x00004000,
+ FLAG_IGNORE_CHEEK_PRESSES = 0x00008000,
+ FLAG_LAYOUT_INSET_DECOR = 0x00010000,
+ FLAG_ALT_FOCUSABLE_IM = 0x00020000,
+ FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000,
+ FLAG_SHOW_WHEN_LOCKED = 0x00080000,
+ FLAG_SHOW_WALLPAPER = 0x00100000,
+ FLAG_TURN_SCREEN_ON = 0x00200000,
+ FLAG_DISMISS_KEYGUARD = 0x00400000,
+ FLAG_IMMERSIVE = 0x00800000,
+ FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000,
+ FLAG_COMPATIBLE_WINDOW = 0x20000000,
+ FLAG_SYSTEM_ERROR = 0x40000000,
+};
+
+// Window types from WindowManager.LayoutParams
+enum {
+ FIRST_APPLICATION_WINDOW = 1,
+ TYPE_BASE_APPLICATION = 1,
+ TYPE_APPLICATION = 2,
+ TYPE_APPLICATION_STARTING = 3,
+ LAST_APPLICATION_WINDOW = 99,
+ FIRST_SUB_WINDOW = 1000,
+ TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
+ TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1,
+ TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
+ TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
+ TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4,
+ LAST_SUB_WINDOW = 1999,
+ FIRST_SYSTEM_WINDOW = 2000,
+ TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
+ TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1,
+ TYPE_PHONE = FIRST_SYSTEM_WINDOW+2,
+ TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3,
+ TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4,
+ TYPE_TOAST = FIRST_SYSTEM_WINDOW+5,
+ TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6,
+ TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7,
+ TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8,
+ TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9,
+ TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10,
+ TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11,
+ TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
+ TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13,
+ TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14,
+ LAST_SYSTEM_WINDOW = 2999,
+};
+
+enum {
+ POWER_MANAGER_OTHER_EVENT = 0,
+ POWER_MANAGER_CHEEK_EVENT = 1,
+ POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either
+ // up events or LONG_TOUCH events.
+ POWER_MANAGER_LONG_TOUCH_EVENT = 3,
+ POWER_MANAGER_TOUCH_UP_EVENT = 4,
+ POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events.
+};
+
+// Delay between reporting long touch events to the power manager.
+const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms
+
+// Default input dispatching timeout if there is no focused application or paused window
+// from which to determine an appropriate dispatching timeout.
+const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+
+// Minimum amount of time to provide to the input dispatcher for delivery of an event
+// regardless of how long the application window was paused.
+const nsecs_t MIN_INPUT_DISPATCHING_TIMEOUT = 1000 * 1000000LL; // 1 sec
+
// ----------------------------------------------------------------------------
static struct {
@@ -53,17 +140,18 @@ static struct {
jmethodID notifyInputChannelBroken;
jmethodID notifyInputChannelANR;
jmethodID notifyInputChannelRecoveredFromANR;
+ jmethodID notifyANR;
jmethodID virtualKeyFeedback;
- jmethodID hackInterceptKey;
+ jmethodID interceptKeyBeforeQueueing;
+ jmethodID interceptKeyBeforeDispatching;
+ jmethodID checkInjectEventsPermission;
jmethodID goToSleep;
- jmethodID pokeUserActivityForKey;
+ jmethodID pokeUserActivity;
jmethodID notifyAppSwitchComing;
jmethodID filterTouchEvents;
jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyDefinitions;
jmethodID getExcludedDeviceNames;
- jmethodID getKeyEventTargets;
- jmethodID getMotionEventTargets;
} gCallbacksClassInfo;
static struct {
@@ -79,9 +167,37 @@ static struct {
static struct {
jclass clazz;
- jmethodID ctor;
- jfieldID mArray;
-} gInputTargetListClassInfo;
+ jfieldID inputChannel;
+ jfieldID layoutParamsFlags;
+ jfieldID layoutParamsType;
+ jfieldID dispatchingTimeoutNanos;
+ jfieldID frameLeft;
+ jfieldID frameTop;
+ jfieldID touchableAreaLeft;
+ jfieldID touchableAreaTop;
+ jfieldID touchableAreaRight;
+ jfieldID touchableAreaBottom;
+ jfieldID visible;
+ jfieldID hasFocus;
+ jfieldID hasWallpaper;
+ jfieldID paused;
+ jfieldID ownerPid;
+ jfieldID ownerUid;
+} gInputWindowClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jfieldID name;
+ jfieldID dispatchingTimeoutNanos;
+ jfieldID token;
+} gInputApplicationClassInfo;
+
+// ----------------------------------------------------------------------------
+
+static inline nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
// ----------------------------------------------------------------------------
@@ -103,6 +219,11 @@ public:
jweak inputChannelObjWeak);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
+ void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
+ void setFocusedApplication(JNIEnv* env, jobject applicationObj);
+ void setInputDispatchMode(bool enabled, bool frozen);
+ void preemptInputDispatch();
+
/* --- InputReaderPolicyInterface implementation --- */
virtual bool getDisplayInfo(int32_t displayId,
@@ -130,16 +251,66 @@ public:
nsecs_t& outNewTimeout);
virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
virtual nsecs_t getKeyRepeatTimeout();
- virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
- virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
private:
+ struct InputWindow {
+ sp<InputChannel> inputChannel;
+ int32_t layoutParamsFlags;
+ int32_t layoutParamsType;
+ nsecs_t dispatchingTimeout;
+ int32_t frameLeft;
+ int32_t frameTop;
+ int32_t touchableAreaLeft;
+ int32_t touchableAreaTop;
+ int32_t touchableAreaRight;
+ int32_t touchableAreaBottom;
+ bool visible;
+ bool hasFocus;
+ bool hasWallpaper;
+ bool paused;
+ int32_t ownerPid;
+ int32_t ownerUid;
+
+ inline bool touchableAreaContainsPoint(int32_t x, int32_t y) {
+ return x >= touchableAreaLeft && x <= touchableAreaRight
+ && y >= touchableAreaTop && y <= touchableAreaBottom;
+ }
+ };
+
+ struct InputApplication {
+ String8 name;
+ nsecs_t dispatchingTimeout;
+ jweak tokenObjWeak;
+ };
+
+ class ANRTimer {
+ enum Budget {
+ SYSTEM = 0,
+ APPLICATION = 1
+ };
+
+ Budget mBudget;
+ nsecs_t mStartTime;
+ bool mFrozen;
+ InputWindow* mPausedWindow;
+
+ public:
+ ANRTimer();
+
+ void dispatchFrozenBySystem();
+ void dispatchPausedByApplication(InputWindow* pausedWindow);
+ bool waitForDispatchStateChangeLd(NativeInputManager* inputManager);
+
+ nsecs_t getTimeSpentWaitingForApplication() const;
+ };
+
sp<InputManager> mInputManager;
jobject mCallbacksObj;
- jobject mReusableInputTargetListObj;
// Cached filtering policies.
int32_t mFilterTouchEvents;
@@ -160,30 +331,78 @@ private:
jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel);
+ // Input target and focus tracking. (lock mDispatchLock)
+ Mutex mDispatchLock;
+ Condition mDispatchStateChanged;
+
+ bool mDispatchEnabled;
+ bool mDispatchFrozen;
+ bool mWindowsReady;
+ Vector<InputWindow> mWindows;
+ Vector<InputWindow*> mWallpaperWindows;
+
+ // Focus tracking for keys, trackball, etc.
+ InputWindow* mFocusedWindow;
+
+ // Focus tracking for touch.
+ bool mTouchDown;
+ InputWindow* mTouchedWindow; // primary target for current down
+ Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets
+
+ Vector<InputWindow*> mTempTouchedOutsideWindows; // temporary outside touch targets
+ Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets
+
+ // Focused application.
+ InputApplication* mFocusedApplication;
+ InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication
+
+ void dumpDispatchStateLd();
+
+ bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout);
+ void releaseFocusedApplicationLd(JNIEnv* env);
+
+ int32_t waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets, InputWindow*& outFocusedWindow);
+ int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets, InputWindow*& outTouchedWindow);
+
+ void releaseTouchedWindowLd();
+
+ int32_t identifyTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+ int32_t identifyTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+
+ void pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType);
+ void pokeUserActivity(nsecs_t eventTime, int32_t eventType);
+ bool checkInjectionPermission(const InputWindow* window,
+ int32_t injectorPid, int32_t injectorUid);
+
+ static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow);
+ static void addTarget(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets);
+
static inline JNIEnv* jniEnv() {
return AndroidRuntime::getJNIEnv();
}
static bool isAppSwitchKey(int32_t keyCode);
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
- static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
- Vector<InputTarget>& outTargets);
};
// ----------------------------------------------------------------------------
NativeInputManager::NativeInputManager(jobject callbacksObj) :
mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
- mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
+ mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1),
+ mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
+ mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
+ mFocusedApplication(NULL) {
JNIEnv* env = jniEnv();
mCallbacksObj = env->NewGlobalRef(callbacksObj);
- jobject inputTargetListObj = env->NewObject(gInputTargetListClassInfo.clazz,
- gInputTargetListClassInfo.ctor);
- mReusableInputTargetListObj = env->NewGlobalRef(inputTargetListObj);
- env->DeleteLocalRef(inputTargetListObj);
-
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
@@ -192,7 +411,8 @@ NativeInputManager::~NativeInputManager() {
JNIEnv* env = jniEnv();
env->DeleteGlobalRef(mCallbacksObj);
- env->DeleteGlobalRef(mReusableInputTargetListObj);
+
+ releaseFocusedApplicationLd(env);
}
bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
@@ -362,7 +582,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
#if DEBUG_INPUT_READER_POLICY
LOGD("interceptKey - when=%lld, deviceId=%d, down=%d, keyCode=%d, scanCode=%d, "
- "policyFlags=%d",
+ "policyFlags=0x%x",
when, deviceId, down, keyCode, scanCode, policyFlags);
#endif
@@ -375,9 +595,10 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
bool isScreenOn = this->isScreenOn();
bool isScreenBright = this->isScreenBright();
- jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey,
+ jint wmActions = env->CallIntMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeQueueing,
deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
- if (checkAndClearExceptionFromCallback(env, "hackInterceptKey")) {
+ if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
wmActions = 0;
}
@@ -398,8 +619,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
}
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
- env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when);
- checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey");
+ pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT);
}
if (wmActions & WM_ACTION_PASS_TO_USER) {
@@ -412,6 +632,9 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
}
}
+
+ // TODO Be smarter about which keys cause us to request interception during dispatch.
+ actions |= InputReaderPolicyInterface::ACTION_INTERCEPT_DISPATCH;
return actions;
}
@@ -423,12 +646,13 @@ int32_t NativeInputManager::interceptTouch(nsecs_t when) {
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
if (isScreenOn()) {
// Only dispatch touch events when the device is awake.
+ // Do not wake the device.
actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
- }
- if (! isScreenBright()) {
- // Brighten the screen if dimmed.
- actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ if (! isScreenBright()) {
+ // Brighten the screen if dimmed.
+ actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ }
}
return actions;
@@ -444,12 +668,13 @@ int32_t NativeInputManager::interceptTrackball(nsecs_t when,
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
if (isScreenOn()) {
// Only dispatch trackball events when the device is awake.
+ // Do not wake the device.
actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
- }
- if (! isScreenBright()) {
- // Brighten the screen if dimmed.
- actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ if (! isScreenBright()) {
+ // Brighten the screen if dimmed.
+ actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ }
}
return actions;
@@ -639,6 +864,27 @@ void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChanne
}
}
+bool NativeInputManager::notifyANR(jobject tokenObj, nsecs_t& outNewTimeout) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyANR");
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ jlong newTimeout = env->CallLongMethod(mCallbacksObj,
+ gCallbacksClassInfo.notifyANR, tokenObj);
+ if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
+ newTimeout = -2;
+ }
+
+ if (newTimeout == -2) {
+ return false; // abort
+ }
+
+ outNewTimeout = newTimeout;
+ return true; // resume
+}
+
nsecs_t NativeInputManager::getKeyRepeatTimeout() {
if (! isScreenOn()) {
// Disable key repeat when the screen is off.
@@ -649,85 +895,898 @@ nsecs_t NativeInputManager::getKeyRepeatTimeout() {
}
}
-int32_t NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
- int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
-#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
- policyFlags, injectorPid, injectorUid);
+void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
+#if DEBUG_FOCUS
+ LOGD("setInputWindows");
#endif
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
- JNIEnv* env = jniEnv();
+ sp<InputChannel> touchedWindowChannel;
+ if (mTouchedWindow) {
+ touchedWindowChannel = mTouchedWindow->inputChannel;
+ mTouchedWindow = NULL;
+ }
+ size_t numTouchedWallpapers = mTouchedWallpaperWindows.size();
+ if (numTouchedWallpapers != 0) {
+ for (size_t i = 0; i < numTouchedWallpapers; i++) {
+ mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel);
+ }
+ mTouchedWallpaperWindows.clear();
+ }
+
+ mWindows.clear();
+ mFocusedWindow = NULL;
+ mWallpaperWindows.clear();
+
+ if (windowObjArray) {
+ mWindowsReady = true;
+
+ jsize length = env->GetArrayLength(windowObjArray);
+ for (jsize i = 0; i < length; i++) {
+ jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i);
+ if (! inputTargetObj) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ mWindows.push();
+ InputWindow& window = mWindows.editTop();
+ bool valid = populateWindow(env, inputTargetObj, window);
+ if (! valid) {
+ mWindows.pop();
+ }
+
+ env->DeleteLocalRef(inputTargetObj);
+ }
+
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(i);
+ if (window->hasFocus) {
+ mFocusedWindow = window;
+ }
+
+ if (window->layoutParamsType == TYPE_WALLPAPER) {
+ mWallpaperWindows.push(window);
+
+ for (size_t j = 0; j < numTouchedWallpapers; j++) {
+ if (window->inputChannel == mTempTouchedWallpaperChannels[i]) {
+ mTouchedWallpaperWindows.push(window);
+ }
+ }
+ }
+
+ if (window->inputChannel == touchedWindowChannel) {
+ mTouchedWindow = window;
+ }
+ }
+ } else {
+ mWindowsReady = false;
+ }
+
+ mTempTouchedWallpaperChannels.clear();
+
+ mDispatchStateChanged.broadcast();
+
+#if DEBUG_FOCUS
+ dumpDispatchStateLd();
+#endif
+ } // release lock
+}
+
+bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj,
+ InputWindow& outWindow) {
+ bool valid = false;
+
+ jobject inputChannelObj = env->GetObjectField(windowObj,
+ gInputWindowClassInfo.inputChannel);
+ if (inputChannelObj) {
+ sp<InputChannel> inputChannel =
+ android_view_InputChannel_getInputChannel(env, inputChannelObj);
+ if (inputChannel != NULL) {
+ jint layoutParamsFlags = env->GetIntField(windowObj,
+ gInputWindowClassInfo.layoutParamsFlags);
+ jint layoutParamsType = env->GetIntField(windowObj,
+ gInputWindowClassInfo.layoutParamsType);
+ jlong dispatchingTimeoutNanos = env->GetLongField(windowObj,
+ gInputWindowClassInfo.dispatchingTimeoutNanos);
+ jint frameLeft = env->GetIntField(windowObj,
+ gInputWindowClassInfo.frameLeft);
+ jint frameTop = env->GetIntField(windowObj,
+ gInputWindowClassInfo.frameTop);
+ jint touchableAreaLeft = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaLeft);
+ jint touchableAreaTop = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaTop);
+ jint touchableAreaRight = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaRight);
+ jint touchableAreaBottom = env->GetIntField(windowObj,
+ gInputWindowClassInfo.touchableAreaBottom);
+ jboolean visible = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.visible);
+ jboolean hasFocus = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.hasFocus);
+ jboolean hasWallpaper = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.hasWallpaper);
+ jboolean paused = env->GetBooleanField(windowObj,
+ gInputWindowClassInfo.paused);
+ jint ownerPid = env->GetIntField(windowObj,
+ gInputWindowClassInfo.ownerPid);
+ jint ownerUid = env->GetIntField(windowObj,
+ gInputWindowClassInfo.ownerUid);
+
+ outWindow.inputChannel = inputChannel;
+ outWindow.layoutParamsFlags = layoutParamsFlags;
+ outWindow.layoutParamsType = layoutParamsType;
+ outWindow.dispatchingTimeout = dispatchingTimeoutNanos;
+ outWindow.frameLeft = frameLeft;
+ outWindow.frameTop = frameTop;
+ outWindow.touchableAreaLeft = touchableAreaLeft;
+ outWindow.touchableAreaTop = touchableAreaTop;
+ outWindow.touchableAreaRight = touchableAreaRight;
+ outWindow.touchableAreaBottom = touchableAreaBottom;
+ outWindow.visible = visible;
+ outWindow.hasFocus = hasFocus;
+ outWindow.hasWallpaper = hasWallpaper;
+ outWindow.paused = paused;
+ outWindow.ownerPid = ownerPid;
+ outWindow.ownerUid = ownerUid;
+ valid = true;
+ } else {
+ LOGW("Dropping input target because its input channel is not initialized.");
+ }
- jint injectionResult;
- jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
- if (! keyEventObj) {
- LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ env->DeleteLocalRef(inputChannelObj);
} else {
- jint injectionResult = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj,
- keyEventObj, jint(keyEvent->getNature()), jint(policyFlags),
- jint(injectorPid), jint(injectorUid));
- if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) {
+ LOGW("Dropping input target because the input channel object was null.");
+ }
+ return valid;
+}
+
+void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) {
+#if DEBUG_FOCUS
+ LOGD("setFocusedApplication");
+#endif
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ releaseFocusedApplicationLd(env);
+
+ if (applicationObj) {
+ jstring nameObj = jstring(env->GetObjectField(applicationObj,
+ gInputApplicationClassInfo.name));
+ jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj,
+ gInputApplicationClassInfo.dispatchingTimeoutNanos);
+ jobject tokenObj = env->GetObjectField(applicationObj,
+ gInputApplicationClassInfo.token);
+ jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj);
+ if (! tokenObjWeak) {
+ LOGE("Could not create weak reference for application token.");
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+ env->DeleteLocalRef(tokenObj);
+
+ mFocusedApplication = & mFocusedApplicationStorage;
+
+ if (nameObj) {
+ const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
+ mFocusedApplication->name.setTo(nameStr);
+ env->ReleaseStringUTFChars(nameObj, nameStr);
+ env->DeleteLocalRef(nameObj);
+ } else {
+ LOGE("InputApplication.name should not be null.");
+ mFocusedApplication->name.setTo("unknown");
+ }
+
+ mFocusedApplication->dispatchingTimeout = dispatchingTimeoutNanos;
+ mFocusedApplication->tokenObjWeak = tokenObjWeak;
+ }
+
+ mDispatchStateChanged.broadcast();
+
+#if DEBUG_FOCUS
+ dumpDispatchStateLd();
+#endif
+ } // release lock
+}
+
+void NativeInputManager::releaseFocusedApplicationLd(JNIEnv* env) {
+ if (mFocusedApplication) {
+ env->DeleteWeakGlobalRef(mFocusedApplication->tokenObjWeak);
+ mFocusedApplication = NULL;
+ }
+}
+
+void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
+#if DEBUG_FOCUS
+ LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+#endif
+
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
+ mDispatchEnabled = enabled;
+ mDispatchFrozen = frozen;
+
+ mDispatchStateChanged.broadcast();
+ }
+
+#if DEBUG_FOCUS
+ dumpDispatchStateLd();
+#endif
+ } // release lock
+}
+
+void NativeInputManager::preemptInputDispatch() {
+#if DEBUG_FOCUS
+ LOGD("preemptInputDispatch");
+#endif
+
+ mInputManager->preemptInputDispatch();
+}
+
+int32_t NativeInputManager::waitForFocusedWindowLd(uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+ InputWindow*& outFocusedWindow) {
+
+ int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ bool firstIteration = true;
+ ANRTimer anrTimer;
+ for (;;) {
+ if (firstIteration) {
+ firstIteration = false;
+ } else {
+ if (! anrTimer.waitForDispatchStateChangeLd(this)) {
+ LOGW("Dropping event because the dispatcher timed out waiting to identify "
+ "the window that should receive it.");
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+ }
+
+ // If dispatch is not enabled then fail.
+ if (! mDispatchEnabled) {
+ LOGI("Dropping event because input dispatch is disabled.");
injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break;
+ }
+
+ // If dispatch is frozen or we don't have valid window data yet then wait.
+ if (mDispatchFrozen || ! mWindowsReady) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because dispatch is frozen or windows are not ready.");
+#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue;
+ }
+
+ // If there is no currently focused window and no focused application
+ // then drop the event.
+ if (! mFocusedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may yet introduce a new target: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ continue;
+ }
+
+ LOGI("Dropping event because there is no focused window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break;
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(mFocusedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ break;
+ }
+
+ // If the currently focused window is paused then keep waiting.
+ if (mFocusedWindow->paused) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because focused window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(mFocusedWindow);
+ continue;
+ }
+
+ // Success!
+ break; // done waiting, exit loop
+ }
+
+ // Output targets.
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ addTarget(mFocusedWindow, InputTarget::FLAG_SYNC,
+ anrTimer.getTimeSpentWaitingForApplication(), outTargets);
+
+ outFocusedWindow = mFocusedWindow;
+ } else {
+ outFocusedWindow = NULL;
+ }
+
+#if DEBUG_FOCUS
+ LOGD("waitForFocusedWindow finished: injectionResult=%d",
+ injectionResult);
+ dumpDispatchStateLd();
+#endif
+ return injectionResult;
+}
+
+int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags,
+ int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets,
+ InputWindow*& outTouchedWindow) {
+ nsecs_t startTime = now();
+
+ int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
+ int32_t action = motionEvent->getAction();
+
+ // For security reasons, we defer updating the touch state until we are sure that
+ // event injection will be allowed.
+ //
+ // FIXME In the original code, screenWasOff could never be set to true.
+ // The reason is that the POLICY_FLAG_WOKE_HERE
+ // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw
+ // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was
+ // actually enqueued using the policyFlags that appeared in the final EV_SYN
+ // events upon which no preprocessing took place. So policyFlags was always 0.
+ // In the new native input dispatcher we're a bit more careful about event
+ // preprocessing so the touches we receive can actually have non-zero policyFlags.
+ // Unfortunately we obtain undesirable behavior.
+ //
+ // Here's what happens:
+ //
+ // When the device dims in anticipation of going to sleep, touches
+ // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause
+ // the device to brighten and reset the user activity timer.
+ // Touches on other windows (such as the launcher window)
+ // are dropped. Then after a moment, the device goes to sleep. Oops.
+ //
+ // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE
+ // instead of POLICY_FLAG_WOKE_HERE...
+ //
+ bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE;
+ bool firstIteration = true;
+ ANRTimer anrTimer;
+ for (;;) {
+ if (firstIteration) {
+ firstIteration = false;
+ } else {
+ if (! anrTimer.waitForDispatchStateChangeLd(this)) {
+ LOGW("Dropping event because the dispatcher timed out waiting to identify "
+ "the window that should receive it.");
+ injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
+ break;
+ }
+ }
+
+ // If dispatch is not enabled then fail.
+ if (! mDispatchEnabled) {
+ LOGI("Dropping event because input dispatch is disabled.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break; // failed, exit wait loop
+ }
+
+ // If dispatch is frozen or we don't have valid window data yet then wait.
+ if (mDispatchFrozen || ! mWindowsReady) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because dispatch is frozen or windows are not ready.");
+#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue;
+ }
+
+ // Update the touch state as needed based on the properties of the touch event.
+ if (action == MOTION_EVENT_ACTION_DOWN) {
+ InputWindow* newTouchedWindow = NULL;
+ mTempTouchedOutsideWindows.clear();
+
+ int32_t x = int32_t(motionEvent->getX(0));
+ int32_t y = int32_t(motionEvent->getY(0));
+ InputWindow* topErrorWindow = NULL;
+
+ // Traverse windows from front to back to find touched window and outside targets.
+ size_t numWindows = mWindows.size();
+ for (size_t i = 0; i < numWindows; i++) {
+ InputWindow* window = & mWindows.editItemAt(i);
+ int32_t flags = window->layoutParamsFlags;
+
+ if (flags & FLAG_SYSTEM_ERROR) {
+ if (! topErrorWindow) {
+ topErrorWindow = window;
+ }
+ }
+
+ if (window->visible) {
+ if (! (flags & FLAG_NOT_TOUCHABLE)) {
+ bool isTouchModal = (flags &
+ (FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL)) == 0;
+ if (isTouchModal || window->touchableAreaContainsPoint(x, y)) {
+ if (! screenWasOff || flags & FLAG_TOUCHABLE_WHEN_WAKING) {
+ newTouchedWindow = window;
+ }
+ break; // found touched window, exit window loop
+ }
+ }
+
+ if (flags & FLAG_WATCH_OUTSIDE_TOUCH) {
+ mTempTouchedOutsideWindows.push(window);
+ }
+ }
+ }
+
+ // If there is an error window but it is not taking focus (typically because
+ // it is invisible) then wait for it. Any other focused window may in
+ // fact be in ANR state.
+ if (topErrorWindow && newTouchedWindow != topErrorWindow) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because system error window is pending.");
+#endif
+ anrTimer.dispatchFrozenBySystem();
+ continue; // wait some more
+ }
+
+ // If we did not find a touched window then fail.
+ if (! newTouchedWindow) {
+ if (mFocusedApplication) {
+#if DEBUG_FOCUS
+ LOGD("Waiting because there is no focused window but there is a "
+ "focused application that may yet introduce a new target: '%s'.",
+ mFocusedApplication->name.string());
+#endif
+ continue;
+ }
+
+ LOGI("Dropping event because there is no touched window or focused application.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break; // failed, exit wait loop
+ }
+
+ // Check permissions.
+ if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ break; // failed, exit wait loop
+ }
+
+ // If the touched window is paused then keep waiting.
+ if (newTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(newTouchedWindow);
+ continue; // wait some more
+ }
+
+ // Success! Update the touch dispatch state for real.
+ releaseTouchedWindowLd();
+
+ mTouchedWindow = newTouchedWindow;
+
+ if (newTouchedWindow->hasWallpaper) {
+ mTouchedWallpaperWindows.appendVector(mWallpaperWindows);
+ }
+ break; // done
} else {
- populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+ // Check permissions.
+ if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) {
+ injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
+ break; // failed, exit wait loop
+ }
+
+ // If there is no currently touched window then fail.
+ if (! mTouchedWindow || ! mTouchDown) {
+ LOGI("Dropping event because touched window is no longer valid.");
+ injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ break; // failed, exit wait loop
+ }
+
+ // If the touched window is paused then keep waiting.
+ if (mTouchedWindow->paused) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("Waiting because touched window is paused.");
+#endif
+ anrTimer.dispatchPausedByApplication(mTouchedWindow);
+ continue; // wait some more
+ }
+
+ // Success!
+ break; // done
}
- env->DeleteLocalRef(keyEventObj);
}
+
+ // Output targets.
+ bool havePermission;
+ if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) {
+ // Injection succeeded so the injector must have permission.
+ havePermission = true;
+
+ size_t numWallpaperWindows = mTouchedWallpaperWindows.size();
+ for (size_t i = 0; i < numWallpaperWindows; i++) {
+ addTarget(mTouchedWallpaperWindows[i], 0, 0, outTargets);
+ }
+
+ size_t numOutsideWindows = mTempTouchedOutsideWindows.size();
+ for (size_t i = 0; i < numOutsideWindows; i++) {
+ addTarget(mTempTouchedOutsideWindows[i], InputTarget::FLAG_OUTSIDE, 0, outTargets);
+ }
+
+ addTarget(mTouchedWindow, InputTarget::FLAG_SYNC,
+ anrTimer.getTimeSpentWaitingForApplication(), outTargets);
+ outTouchedWindow = mTouchedWindow;
+ } else {
+ if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED
+ && checkInjectionPermission(NULL, injectorPid, injectorUid)) {
+ // Injection failed but the injector does have permission to inject events.
+ // While we might not have found a valid target for the injected event, we
+ // still want to update the dispatch state to take it into account.
+ havePermission = true;
+ } else {
+ // Injector does not have permission to inject events.
+ // We make sure to leave the dispatch state unchanged.
+ havePermission = false;
+ }
+ outTouchedWindow = NULL;
+ }
+ mTempTouchedOutsideWindows.clear();
+
+ // Update final pieces of touch state now that we know for sure whether the injector
+ // had permission to perform the injection.
+ if (havePermission) {
+ if (action == MOTION_EVENT_ACTION_DOWN) {
+ if (mTouchDown) {
+ // This is weird. We got a down but we thought it was already down!
+ LOGW("Pointer down received while already down.");
+ } else {
+ mTouchDown = true;
+ }
+
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ // Since we failed to identify a target for this touch down, we may still
+ // be holding on to an earlier target from a previous touch down. Release it.
+ releaseTouchedWindowLd();
+ }
+ } else if (action == MOTION_EVENT_ACTION_UP) {
+ mTouchDown = false;
+ releaseTouchedWindowLd();
+ }
+ }
+
+#if DEBUG_FOCUS
+ LOGD("waitForTouchedWindow finished: injectionResult=%d",
+ injectionResult);
+ dumpDispatchStateLd();
+#endif
return injectionResult;
}
-int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+void NativeInputManager::releaseTouchedWindowLd() {
+ mTouchedWindow = NULL;
+ mTouchedWallpaperWindows.clear();
+}
+
+void NativeInputManager::addTarget(const InputWindow* window, int32_t targetFlags,
+ nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& outTargets) {
+ nsecs_t timeout = window->dispatchingTimeout - timeSpentWaitingForApplication;
+ if (timeout < MIN_INPUT_DISPATCHING_TIMEOUT) {
+ timeout = MIN_INPUT_DISPATCHING_TIMEOUT;
+ }
+
+ outTargets.push();
+
+ InputTarget& target = outTargets.editTop();
+ target.inputChannel = window->inputChannel;
+ target.flags = targetFlags;
+ target.timeout = timeout;
+ target.xOffset = - window->frameLeft;
+ target.yOffset = - window->frameTop;
+}
+
+bool NativeInputManager::checkInjectionPermission(const InputWindow* window,
+ int32_t injectorPid, int32_t injectorUid) {
+ if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) {
+ JNIEnv* env = jniEnv();
+ jboolean result = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid);
+ checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission");
+
+ if (! result) {
+ if (window) {
+ LOGW("Permission denied: injecting event from pid %d uid %d to window "
+ "with input channel %s owned by uid %d",
+ injectorPid, injectorUid, window->inputChannel->getName().string(),
+ window->ownerUid);
+ } else {
+ LOGW("Permission denied: injecting event from pid %d uid %d",
+ injectorPid, injectorUid);
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
#if DEBUG_INPUT_DISPATCHER_POLICY
- LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ LOGD("waitForKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
policyFlags, injectorPid, injectorUid);
#endif
- JNIEnv* env = jniEnv();
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
- jint injectionResult;
- jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
- if (! motionEventObj) {
- LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
- } else {
- jint injectionResult = env->CallIntMethod(mCallbacksObj,
- gCallbacksClassInfo.getMotionEventTargets, mReusableInputTargetListObj,
- motionEventObj, jint(motionEvent->getNature()), jint(policyFlags),
- jint(injectorPid), jint(injectorUid));
- if (checkAndClearExceptionFromCallback(env, "getMotionEventTargets")) {
- injectionResult = INPUT_EVENT_INJECTION_FAILED;
+ InputWindow* focusedWindow;
+ int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = focusedWindow->layoutParamsType;
+ } // release lock
+
+ if (policyFlags & POLICY_FLAG_INTERCEPT_DISPATCH) {
+ const InputTarget& target = outTargets.top();
+
+ JNIEnv* env = jniEnv();
+
+ jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel);
+ if (inputChannelObj) {
+ jboolean consumed = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.interceptKeyBeforeDispatching,
+ inputChannelObj, keyEvent->getKeyCode(), keyEvent->getMetaState(),
+ keyEvent->getAction() == KEY_EVENT_ACTION_DOWN,
+ keyEvent->getRepeatCount(), policyFlags);
+ bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatch");
+
+ env->DeleteLocalRef(inputChannelObj);
+
+ if (error) {
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+
+ if (consumed) {
+ outTargets.clear();
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+ }
} else {
- populateInputTargets(env, mReusableInputTargetListObj, outTargets);
+ LOGW("Could not apply key dispatch policy because input channel '%s' is "
+ "no longer valid.", target.inputChannel->getName().string());
}
- android_view_MotionEvent_recycle(env, motionEventObj);
- env->DeleteLocalRef(motionEventObj);
}
- return injectionResult;
+
+ pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ switch (motionEvent->getNature()) {
+ case INPUT_EVENT_NATURE_TRACKBALL:
+ return identifyTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ outTargets);
+
+ case INPUT_EVENT_NATURE_TOUCH:
+ return identifyTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid,
+ outTargets);
+
+ default:
+ assert(false);
+ return INPUT_EVENT_INJECTION_FAILED;
+ }
+}
+
+int32_t NativeInputManager::identifyTrackballEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("identifyTrackballEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ InputWindow* focusedWindow;
+ int32_t injectionResult = waitForFocusedWindowLd(policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ focusedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = focusedWindow->layoutParamsType;
+ } // release lock
+
+ pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT);
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
}
-void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
+int32_t NativeInputManager::identifyTouchEventTargets(MotionEvent* motionEvent,
+ uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) {
- jobjectArray inputTargetArray = jobjectArray(env->GetObjectField(
- inputTargetListObj, gInputTargetListClassInfo.mArray));
-
- jsize length = env->GetArrayLength(inputTargetArray);
- for (jsize i = 0; i < length; i++) {
- jobject item = env->GetObjectArrayElement(inputTargetArray, i);
- if (! item) {
- break; // found null element indicating end of used portion of the array
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("identifyTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
+ policyFlags, injectorPid, injectorUid);
+#endif
+
+ int32_t windowType;
+ { // acquire lock
+ AutoMutex _l(mDispatchLock);
+
+ InputWindow* touchedWindow;
+ int32_t injectionResult = waitForTouchedWindowLd(motionEvent, policyFlags,
+ injectorPid, injectorUid, outTargets, /*out*/ touchedWindow);
+ if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
+ return injectionResult;
+ }
+
+ windowType = touchedWindow->layoutParamsType;
+ } // release lock
+
+ int32_t eventType;
+ switch (motionEvent->getAction()) {
+ case MOTION_EVENT_ACTION_DOWN:
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ break;
+ case MOTION_EVENT_ACTION_UP:
+ eventType = POWER_MANAGER_TOUCH_UP_EVENT;
+ break;
+ default:
+ if (motionEvent->getEventTime() - motionEvent->getDownTime()
+ >= EVENT_IGNORE_DURATION) {
+ eventType = POWER_MANAGER_TOUCH_EVENT;
+ } else {
+ eventType = POWER_MANAGER_LONG_TOUCH_EVENT;
}
+ break;
+ }
+ pokeUserActivityIfNeeded(windowType, eventType);
+ return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) {
+ if (windowType != TYPE_KEYGUARD) {
+ nsecs_t eventTime = now();
+ pokeUserActivity(eventTime, eventType);
+ }
+}
- outTargets.add();
- android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
+ JNIEnv* env = jniEnv();
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivity,
+ eventTime, eventType);
+ checkAndClearExceptionFromCallback(env, "pokeUserActivity");
+}
- env->DeleteLocalRef(item);
+void NativeInputManager::dumpDispatchStateLd() {
+#if DEBUG_FOCUS
+ LOGD(" dispatcherState: dispatchEnabled=%d, dispatchFrozen=%d, windowsReady=%d",
+ mDispatchEnabled, mDispatchFrozen, mWindowsReady);
+ if (mFocusedApplication) {
+ LOGD(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms",
+ mFocusedApplication->name.string(),
+ mFocusedApplication->dispatchingTimeout / 1000000.0);
+ } else {
+ LOGD(" focusedApplication: <null>");
+ }
+ LOGD(" focusedWindow: '%s'",
+ mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>");
+ LOGD(" touchedWindow: '%s', touchDown=%d",
+ mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>",
+ mTouchDown);
+ for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) {
+ LOGD(" touchedWallpaperWindows[%d]: '%s'",
+ i, mTouchedWallpaperWindows[i]->inputChannel->getName().string());
}
- env->DeleteLocalRef(inputTargetArray);
+ for (size_t i = 0; i < mWindows.size(); i++) {
+ LOGD(" windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, visible=%d, "
+ "flags=0x%08x, type=0x%08x, "
+ "frame=[%d,%d], touchableArea=[%d,%d][%d,%d], "
+ "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms",
+ i, mWindows[i].inputChannel->getName().string(),
+ mWindows[i].paused, mWindows[i].hasFocus, mWindows[i].hasWallpaper,
+ mWindows[i].visible, mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType,
+ mWindows[i].frameLeft, mWindows[i].frameTop,
+ mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop,
+ mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom,
+ mWindows[i].ownerPid, mWindows[i].ownerUid,
+ mWindows[i].dispatchingTimeout / 1000000.0);
+ }
+#endif
}
+// ----------------------------------------------------------------------------
+
+NativeInputManager::ANRTimer::ANRTimer() :
+ mBudget(APPLICATION), mStartTime(now()), mFrozen(false), mPausedWindow(NULL) {
+}
+
+void NativeInputManager::ANRTimer::dispatchFrozenBySystem() {
+ mFrozen = true;
+}
+
+void NativeInputManager::ANRTimer::dispatchPausedByApplication(InputWindow* pausedWindow) {
+ mPausedWindow = pausedWindow;
+}
+
+bool NativeInputManager::ANRTimer::waitForDispatchStateChangeLd(NativeInputManager* inputManager) {
+ nsecs_t currentTime = now();
+
+ Budget newBudget;
+ nsecs_t dispatchingTimeout;
+ sp<InputChannel> pausedChannel = NULL;
+ jobject tokenObj = NULL;
+ if (mFrozen) {
+ newBudget = SYSTEM;
+ dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+ mFrozen = false;
+ } else if (mPausedWindow) {
+ newBudget = APPLICATION;
+ dispatchingTimeout = mPausedWindow->dispatchingTimeout;
+ pausedChannel = mPausedWindow->inputChannel;
+ mPausedWindow = NULL;
+ } else if (inputManager->mFocusedApplication) {
+ newBudget = APPLICATION;
+ dispatchingTimeout = inputManager->mFocusedApplication->dispatchingTimeout;
+ tokenObj = jniEnv()->NewLocalRef(inputManager->mFocusedApplication->tokenObjWeak);
+ } else {
+ newBudget = APPLICATION;
+ dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
+ }
+
+ if (mBudget != newBudget) {
+ mBudget = newBudget;
+ mStartTime = currentTime;
+ }
+
+ bool result = false;
+ nsecs_t timeoutRemaining = mStartTime + dispatchingTimeout - currentTime;
+ if (timeoutRemaining > 0
+ && inputManager->mDispatchStateChanged.waitRelative(inputManager->mDispatchLock,
+ timeoutRemaining) == OK) {
+ result = true;
+ } else {
+ if (pausedChannel != NULL || tokenObj != NULL) {
+ bool resumed;
+ nsecs_t newTimeout = 0;
+
+ inputManager->mDispatchLock.unlock(); // release lock
+ if (pausedChannel != NULL) {
+ resumed = inputManager->notifyInputChannelANR(pausedChannel, /*out*/ newTimeout);
+ } else {
+ resumed = inputManager->notifyANR(tokenObj, /*out*/ newTimeout);
+ }
+ inputManager->mDispatchLock.lock(); // re-acquire lock
+
+ if (resumed) {
+ mStartTime = now() - dispatchingTimeout + newTimeout;
+ result = true;
+ }
+ }
+ }
+
+ if (tokenObj) {
+ jniEnv()->DeleteLocalRef(tokenObj);
+ }
+
+ return result;
+}
+
+nsecs_t NativeInputManager::ANRTimer::getTimeSpentWaitingForApplication() const {
+ return mBudget == APPLICATION ? now() - mStartTime : 0;
+}
// ----------------------------------------------------------------------------
@@ -926,6 +1985,42 @@ static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jcl
injectorPid, injectorUid, sync, timeoutMillis);
}
+static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz,
+ jobjectArray windowObjArray) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setInputWindows(env, windowObjArray);
+}
+
+static void android_server_InputManager_nativeSetFocusedApplication(JNIEnv* env, jclass clazz,
+ jobject applicationObj) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setFocusedApplication(env, applicationObj);
+}
+
+static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env,
+ jclass clazz, jboolean enabled, jboolean frozen) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->setInputDispatchMode(enabled, frozen);
+}
+
+static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env,
+ jclass clazz) {
+ if (checkInputManagerUnitialized(env)) {
+ return;
+ }
+
+ gNativeInputManager->preemptInputDispatch();
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gInputManagerMethods[] = {
@@ -953,7 +2048,15 @@ static JNINativeMethod gInputManagerMethods[] = {
{ "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I",
(void*) android_server_InputManager_nativeInjectKeyEvent },
{ "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I",
- (void*) android_server_InputManager_nativeInjectMotionEvent }
+ (void*) android_server_InputManager_nativeInjectMotionEvent },
+ { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V",
+ (void*) android_server_InputManager_nativeSetInputWindows },
+ { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V",
+ (void*) android_server_InputManager_nativeSetFocusedApplication },
+ { "nativeSetInputDispatchMode", "(ZZ)V",
+ (void*) android_server_InputManager_nativeSetInputDispatchMode },
+ { "nativePreemptInputDispatch", "()V",
+ (void*) android_server_InputManager_nativePreemptInputDispatch }
};
#define FIND_CLASS(var, className) \
@@ -999,17 +2102,26 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz,
"notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V");
+ GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz,
+ "notifyANR", "(Ljava/lang/Object;)J");
+
GET_METHOD_ID(gCallbacksClassInfo.virtualKeyFeedback, gCallbacksClassInfo.clazz,
"virtualKeyFeedback", "(JIIIIIIJ)V");
- GET_METHOD_ID(gCallbacksClassInfo.hackInterceptKey, gCallbacksClassInfo.clazz,
- "hackInterceptKey", "(IIIIIIJZ)I");
+ GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz,
+ "interceptKeyBeforeQueueing", "(IIIIIIJZ)I");
+
+ GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz,
+ "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIZII)Z");
+
+ GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz,
+ "checkInjectEventsPermission", "(II)Z");
GET_METHOD_ID(gCallbacksClassInfo.goToSleep, gCallbacksClassInfo.clazz,
"goToSleep", "(J)V");
- GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivityForKey, gCallbacksClassInfo.clazz,
- "pokeUserActivityForKey", "(J)V");
+ GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivity, gCallbacksClassInfo.clazz,
+ "pokeUserActivity", "(JI)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
"notifyAppSwitchComing", "()V");
@@ -1027,14 +2139,6 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
- GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz,
- "getKeyEventTargets",
- "(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I");
-
- GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz,
- "getMotionEventTargets",
- "(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I");
-
// VirtualKeyDefinition
FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
@@ -1055,15 +2159,71 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
"height", "I");
- // InputTargetList
+ // InputWindow
+
+ FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow");
+
+ GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz,
+ "inputChannel", "Landroid/view/InputChannel;");
+
+ GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz,
+ "layoutParamsFlags", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, gInputWindowClassInfo.clazz,
+ "layoutParamsType", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, gInputWindowClassInfo.clazz,
+ "dispatchingTimeoutNanos", "J");
+
+ GET_FIELD_ID(gInputWindowClassInfo.frameLeft, gInputWindowClassInfo.clazz,
+ "frameLeft", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz,
+ "frameTop", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaLeft, gInputWindowClassInfo.clazz,
+ "touchableAreaLeft", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaTop, gInputWindowClassInfo.clazz,
+ "touchableAreaTop", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaRight, gInputWindowClassInfo.clazz,
+ "touchableAreaRight", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.touchableAreaBottom, gInputWindowClassInfo.clazz,
+ "touchableAreaBottom", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz,
+ "visible", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz,
+ "hasFocus", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, gInputWindowClassInfo.clazz,
+ "hasWallpaper", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz,
+ "paused", "Z");
+
+ GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz,
+ "ownerPid", "I");
+
+ GET_FIELD_ID(gInputWindowClassInfo.ownerUid, gInputWindowClassInfo.clazz,
+ "ownerUid", "I");
+
+ // InputApplication
+
+ FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/InputApplication");
- FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList");
+ GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz,
+ "name", "Ljava/lang/String;");
- GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz,
- "<init>", "()V");
+ GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos,
+ gInputApplicationClassInfo.clazz,
+ "dispatchingTimeoutNanos", "J");
- GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz,
- "mArray", "[Landroid/view/InputTarget;");
+ GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz,
+ "token", "Ljava/lang/Object;");
return 0;
}