Files
frameworks_base/services/jni/com_android_server_InputManager.cpp
Jeff Brown 0b72e82c5f Fix native input dispatch in the emulator.
Set a default orientation of ROTATION_0.
Added some more careful checks based on whether we have valid
absolute axis information from the driver.
Reset key repeating during configuration changes since the keyboard
device may have been removed.

Change-Id: I685960828acffcb17595fc5683309e8064a76714
2010-06-29 16:52:21 -07:00

2232 lines
82 KiB
C++

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "InputManager-JNI"
//#define LOG_NDEBUG 0
// Log debug messages about InputReaderPolicy
#define DEBUG_INPUT_READER_POLICY 0
// Log debug messages about InputDispatcherPolicy
#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>
#include <ui/InputManager.h>
#include <ui/InputTransport.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include "../../core/jni/android_view_KeyEvent.h"
#include "../../core/jni/android_view_MotionEvent.h"
#include "../../core/jni/android_view_InputChannel.h"
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 {
jclass clazz;
jmethodID isScreenOn;
jmethodID isScreenBright;
jmethodID notifyConfigurationChanged;
jmethodID notifyLidSwitchChanged;
jmethodID notifyInputChannelBroken;
jmethodID notifyInputChannelANR;
jmethodID notifyInputChannelRecoveredFromANR;
jmethodID notifyANR;
jmethodID virtualKeyFeedback;
jmethodID interceptKeyBeforeQueueing;
jmethodID interceptKeyBeforeDispatching;
jmethodID checkInjectEventsPermission;
jmethodID goToSleep;
jmethodID pokeUserActivity;
jmethodID notifyAppSwitchComing;
jmethodID filterTouchEvents;
jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyDefinitions;
jmethodID getExcludedDeviceNames;
} gCallbacksClassInfo;
static struct {
jclass clazz;
jfieldID scanCode;
jfieldID centerX;
jfieldID centerY;
jfieldID width;
jfieldID height;
} gVirtualKeyDefinitionClassInfo;
static struct {
jclass clazz;
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);
}
// ----------------------------------------------------------------------------
class NativeInputManager : public virtual RefBase,
public virtual InputReaderPolicyInterface,
public virtual InputDispatcherPolicyInterface {
protected:
virtual ~NativeInputManager();
public:
NativeInputManager(jobject callbacksObj);
inline sp<InputManager> getInputManager() const { return mInputManager; }
void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
void setDisplayOrientation(int32_t displayId, int32_t orientation);
status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
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,
int32_t* width, int32_t* height, int32_t* orientation);
virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime);
virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
bool rolled);
virtual int32_t interceptTouch(nsecs_t when);
virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue);
virtual bool filterTouchEvents();
virtual bool filterJumpyTouchEvents();
virtual void getVirtualKeyDefinitions(const String8& deviceName,
Vector<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions);
virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
/* --- InputDispatcherPolicyInterface implementation --- */
virtual void notifyConfigurationChanged(nsecs_t when);
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
nsecs_t& outNewTimeout);
virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
virtual nsecs_t getKeyRepeatTimeout();
virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
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;
// Cached filtering policies.
int32_t mFilterTouchEvents;
int32_t mFilterJumpyTouchEvents;
// Cached display state. (lock mDisplayLock)
Mutex mDisplayLock;
int32_t mDisplayWidth, mDisplayHeight;
int32_t mDisplayOrientation;
// Callbacks.
bool isScreenOn();
bool isScreenBright();
// Weak references to all currently registered input channels by receive fd.
Mutex mInputChannelRegistryLock;
KeyedVector<int, jweak> mInputChannelObjWeakByReceiveFd;
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);
};
// ----------------------------------------------------------------------------
NativeInputManager::NativeInputManager(jobject callbacksObj) :
mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
mFocusedApplication(NULL) {
JNIEnv* env = jniEnv();
mCallbacksObj = env->NewGlobalRef(callbacksObj);
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}
NativeInputManager::~NativeInputManager() {
JNIEnv* env = jniEnv();
env->DeleteGlobalRef(mCallbacksObj);
releaseFocusedApplicationLd(env);
}
bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
}
bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) {
LOGE("An exception was thrown by callback '%s'.", methodName);
LOGE_EX(env);
env->ExceptionClear();
return true;
}
return false;
}
void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
if (displayId == 0) {
AutoMutex _l(mDisplayLock);
mDisplayWidth = width;
mDisplayHeight = height;
}
}
void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orientation) {
if (displayId == 0) {
AutoMutex _l(mDisplayLock);
mDisplayOrientation = orientation;
}
}
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
const sp<InputChannel>& inputChannel, jobject inputChannelObj) {
jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj);
if (! inputChannelObjWeak) {
LOGE("Could not create weak reference for input channel.");
LOGE_EX(env);
return NO_MEMORY;
}
status_t status;
{
AutoMutex _l(mInputChannelRegistryLock);
ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
inputChannel->getReceivePipeFd());
if (index >= 0) {
LOGE("Input channel object '%s' has already been registered",
inputChannel->getName().string());
status = INVALID_OPERATION;
goto DeleteWeakRef;
}
mInputChannelObjWeakByReceiveFd.add(inputChannel->getReceivePipeFd(),
inputChannelObjWeak);
}
status = mInputManager->registerInputChannel(inputChannel);
if (! status) {
return OK;
}
{
AutoMutex _l(mInputChannelRegistryLock);
mInputChannelObjWeakByReceiveFd.removeItem(inputChannel->getReceivePipeFd());
}
DeleteWeakRef:
env->DeleteWeakGlobalRef(inputChannelObjWeak);
return status;
}
status_t NativeInputManager::unregisterInputChannel(JNIEnv* env,
const sp<InputChannel>& inputChannel) {
jweak inputChannelObjWeak;
{
AutoMutex _l(mInputChannelRegistryLock);
ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
inputChannel->getReceivePipeFd());
if (index < 0) {
LOGE("Input channel object '%s' is not currently registered",
inputChannel->getName().string());
return INVALID_OPERATION;
}
inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
mInputChannelObjWeakByReceiveFd.removeItemsAt(index);
}
env->DeleteWeakGlobalRef(inputChannelObjWeak);
return mInputManager->unregisterInputChannel(inputChannel);
}
jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env,
const sp<InputChannel>& inputChannel) {
{
AutoMutex _l(mInputChannelRegistryLock);
ssize_t index = mInputChannelObjWeakByReceiveFd.indexOfKey(
inputChannel->getReceivePipeFd());
if (index < 0) {
return NULL;
}
jweak inputChannelObjWeak = mInputChannelObjWeakByReceiveFd.valueAt(index);
return env->NewLocalRef(inputChannelObjWeak);
}
}
bool NativeInputManager::getDisplayInfo(int32_t displayId,
int32_t* width, int32_t* height, int32_t* orientation) {
bool result = false;
if (displayId == 0) {
AutoMutex _l(mDisplayLock);
if (mDisplayWidth > 0) {
*width = mDisplayWidth;
*height = mDisplayHeight;
*orientation = mDisplayOrientation;
result = true;
}
}
return result;
}
bool NativeInputManager::isScreenOn() {
JNIEnv* env = jniEnv();
jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn);
if (checkAndClearExceptionFromCallback(env, "isScreenOn")) {
return true;
}
return result;
}
bool NativeInputManager::isScreenBright() {
JNIEnv* env = jniEnv();
jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright);
if (checkAndClearExceptionFromCallback(env, "isScreenBright")) {
return true;
}
return result;
}
void NativeInputManager::virtualKeyFeedback(nsecs_t when, int32_t deviceId,
int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, nsecs_t downTime) {
#if DEBUG_INPUT_READER_POLICY
LOGD("virtualKeyFeedback - when=%lld, deviceId=%d, action=%d, flags=%d, keyCode=%d, "
"scanCode=%d, metaState=%d, downTime=%lld",
when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
#endif
JNIEnv* env = jniEnv();
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback,
when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
checkAndClearExceptionFromCallback(env, "virtualKeyFeedback");
}
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=0x%x",
when, deviceId, down, keyCode, scanCode, policyFlags);
#endif
const int32_t WM_ACTION_PASS_TO_USER = 1;
const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
const int32_t WM_ACTION_GO_TO_SLEEP = 4;
JNIEnv* env = jniEnv();
bool isScreenOn = this->isScreenOn();
bool isScreenBright = this->isScreenBright();
jint wmActions = env->CallIntMethod(mCallbacksObj,
gCallbacksClassInfo.interceptKeyBeforeQueueing,
deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
wmActions = 0;
}
int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
if (! isScreenOn) {
// Key presses and releases wake the device.
actions |= InputReaderPolicyInterface::ACTION_WOKE_HERE;
}
if (! isScreenBright) {
// Key presses and releases brighten the screen if dimmed.
actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
}
if (wmActions & WM_ACTION_GO_TO_SLEEP) {
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when);
checkAndClearExceptionFromCallback(env, "goToSleep");
}
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT);
}
if (wmActions & WM_ACTION_PASS_TO_USER) {
actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
if (down && isAppSwitchKey(keyCode)) {
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing");
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;
}
int32_t NativeInputManager::interceptTouch(nsecs_t when) {
#if DEBUG_INPUT_READER_POLICY
LOGD("interceptTouch - when=%lld", when);
#endif
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;
}
}
return actions;
}
int32_t NativeInputManager::interceptTrackball(nsecs_t when,
bool buttonChanged, bool buttonDown, bool rolled) {
#if DEBUG_INPUT_READER_POLICY
LOGD("interceptTrackball - when=%lld, buttonChanged=%d, buttonDown=%d, rolled=%d",
when, buttonChanged, buttonDown, rolled);
#endif
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;
}
}
return actions;
}
int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode,
int32_t switchValue) {
#if DEBUG_INPUT_READER_POLICY
LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d",
when, switchCode, switchValue);
#endif
JNIEnv* env = jniEnv();
switch (switchCode) {
case SW_LID:
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
when, switchValue == 0);
checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged");
break;
}
return InputReaderPolicyInterface::ACTION_NONE;
}
bool NativeInputManager::filterTouchEvents() {
if (mFilterTouchEvents < 0) {
JNIEnv* env = jniEnv();
jboolean result = env->CallBooleanMethod(mCallbacksObj,
gCallbacksClassInfo.filterTouchEvents);
if (checkAndClearExceptionFromCallback(env, "filterTouchEvents")) {
result = false;
}
mFilterTouchEvents = result ? 1 : 0;
}
return mFilterTouchEvents;
}
bool NativeInputManager::filterJumpyTouchEvents() {
if (mFilterJumpyTouchEvents < 0) {
JNIEnv* env = jniEnv();
jboolean result = env->CallBooleanMethod(mCallbacksObj,
gCallbacksClassInfo.filterJumpyTouchEvents);
if (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) {
result = false;
}
mFilterJumpyTouchEvents = result ? 1 : 0;
}
return mFilterJumpyTouchEvents;
}
void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName,
Vector<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions) {
JNIEnv* env = jniEnv();
jstring deviceNameStr = env->NewStringUTF(deviceName.string());
if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
jsize length = env->GetArrayLength(result);
for (jsize i = 0; i < length; i++) {
jobject item = env->GetObjectArrayElement(result, i);
outVirtualKeyDefinitions.add();
outVirtualKeyDefinitions.editTop().scanCode =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
outVirtualKeyDefinitions.editTop().centerX =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
outVirtualKeyDefinitions.editTop().centerY =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
outVirtualKeyDefinitions.editTop().width =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
outVirtualKeyDefinitions.editTop().height =
int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
env->DeleteLocalRef(item);
}
env->DeleteLocalRef(result);
}
env->DeleteLocalRef(deviceNameStr);
}
}
void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
JNIEnv* env = jniEnv();
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
gCallbacksClassInfo.getExcludedDeviceNames));
if (! checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
jsize length = env->GetArrayLength(result);
for (jsize i = 0; i < length; i++) {
jstring item = jstring(env->GetObjectArrayElement(result, i));
const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
outExcludedDeviceNames.add(String8(deviceNameChars));
env->ReleaseStringUTFChars(item, deviceNameChars);
env->DeleteLocalRef(item);
}
env->DeleteLocalRef(result);
}
}
void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("notifyConfigurationChanged - when=%lld", when);
#endif
JNIEnv* env = jniEnv();
InputConfiguration config;
mInputManager->getInputConfiguration(& config);
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
when, config.touchScreen, config.keyboard, config.navigation);
checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
}
void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
#endif
JNIEnv* env = jniEnv();
jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
if (inputChannelObjLocal) {
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelBroken,
inputChannelObjLocal);
checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");
env->DeleteLocalRef(inputChannelObjLocal);
}
}
bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
nsecs_t& outNewTimeout) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("notifyInputChannelANR - inputChannel='%s'",
inputChannel->getName().string());
#endif
JNIEnv* env = jniEnv();
jlong newTimeout;
jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
if (inputChannelObjLocal) {
newTimeout = env->CallLongMethod(mCallbacksObj,
gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal);
if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) {
newTimeout = -2;
}
env->DeleteLocalRef(inputChannelObjLocal);
} else {
newTimeout = -2;
}
if (newTimeout == -2) {
return false; // abort
}
outNewTimeout = newTimeout;
return true; // resume
}
void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
#if DEBUG_INPUT_DISPATCHER_POLICY
LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'",
inputChannel->getName().string());
#endif
JNIEnv* env = jniEnv();
jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
if (inputChannelObjLocal) {
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR,
inputChannelObjLocal);
checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR");
env->DeleteLocalRef(inputChannelObjLocal);
}
}
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.
return -1;
} else {
// TODO use ViewConfiguration.getLongPressTimeout()
return milliseconds_to_nanoseconds(500);
}
}
void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
#if DEBUG_FOCUS
LOGD("setInputWindows");
#endif
{ // acquire lock
AutoMutex _l(mDispatchLock);
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.");
}
env->DeleteLocalRef(inputChannelObj);
} else {
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 {
// 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
}
}
// 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;
}
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("waitForKeyEventTargets - 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
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 {
LOGW("Could not apply key dispatch policy because input channel '%s' is "
"no longer valid.", target.inputChannel->getName().string());
}
}
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;
}
int32_t NativeInputManager::identifyTouchEventTargets(MotionEvent* motionEvent,
uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) {
#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);
}
}
void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
JNIEnv* env = jniEnv();
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivity,
eventTime, eventType);
checkAndClearExceptionFromCallback(env, "pokeUserActivity");
}
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());
}
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;
}
// ----------------------------------------------------------------------------
static sp<NativeInputManager> gNativeInputManager;
static bool checkInputManagerUnitialized(JNIEnv* env) {
if (gNativeInputManager == NULL) {
LOGE("Input manager not initialized.");
jniThrowRuntimeException(env, "Input manager not initialized.");
return true;
}
return false;
}
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
jobject callbacks) {
if (gNativeInputManager == NULL) {
gNativeInputManager = new NativeInputManager(callbacks);
} else {
LOGE("Input manager already initialized.");
jniThrowRuntimeException(env, "Input manager already initialized.");
}
}
static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {
if (checkInputManagerUnitialized(env)) {
return;
}
status_t result = gNativeInputManager->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
}
static void android_server_InputManager_nativeSetDisplaySize(JNIEnv* env, jclass clazz,
jint displayId, jint width, jint height) {
if (checkInputManagerUnitialized(env)) {
return;
}
// XXX we could get this from the SurfaceFlinger directly instead of requiring it
// to be passed in like this, not sure which is better but leaving it like this
// keeps the window manager in direct control of when display transitions propagate down
// to the input dispatcher
gNativeInputManager->setDisplaySize(displayId, width, height);
}
static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, jclass clazz,
jint displayId, jint orientation) {
if (checkInputManagerUnitialized(env)) {
return;
}
gNativeInputManager->setDisplayOrientation(displayId, orientation);
}
static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
jint deviceId, jint deviceClasses, jint scanCode) {
if (checkInputManagerUnitialized(env)) {
return KEY_STATE_UNKNOWN;
}
return gNativeInputManager->getInputManager()->getScanCodeState(
deviceId, deviceClasses, scanCode);
}
static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
jint deviceId, jint deviceClasses, jint keyCode) {
if (checkInputManagerUnitialized(env)) {
return KEY_STATE_UNKNOWN;
}
return gNativeInputManager->getInputManager()->getKeyCodeState(
deviceId, deviceClasses, keyCode);
}
static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz,
jint deviceId, jint deviceClasses, jint sw) {
if (checkInputManagerUnitialized(env)) {
return KEY_STATE_UNKNOWN;
}
return gNativeInputManager->getInputManager()->getSwitchState(deviceId, deviceClasses, sw);
}
static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz,
jintArray keyCodes, jbooleanArray outFlags) {
if (checkInputManagerUnitialized(env)) {
return JNI_FALSE;
}
int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
jsize numCodes = env->GetArrayLength(keyCodes);
jboolean result;
if (numCodes == env->GetArrayLength(outFlags)) {
result = gNativeInputManager->getInputManager()->hasKeys(numCodes, codes, flags);
} else {
result = JNI_FALSE;
}
env->ReleaseBooleanArrayElements(outFlags, flags, 0);
env->ReleaseIntArrayElements(keyCodes, codes, 0);
return result;
}
static void throwInputChannelNotInitialized(JNIEnv* env) {
jniThrowException(env, "java/lang/IllegalStateException",
"inputChannel is not initialized");
}
static void android_server_InputManager_handleInputChannelDisposed(JNIEnv* env,
jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data) {
LOGW("Input channel object '%s' was disposed without first being unregistered with "
"the input manager!", inputChannel->getName().string());
if (gNativeInputManager != NULL) {
gNativeInputManager->unregisterInputChannel(env, inputChannel);
}
}
static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
jobject inputChannelObj) {
if (checkInputManagerUnitialized(env)) {
return;
}
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
status_t status = gNativeInputManager->registerInputChannel(
env, inputChannel, inputChannelObj);
if (status) {
jniThrowRuntimeException(env, "Failed to register input channel. "
"Check logs for details.");
return;
}
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
android_server_InputManager_handleInputChannelDisposed, NULL);
}
static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz,
jobject inputChannelObj) {
if (checkInputManagerUnitialized(env)) {
return;
}
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel);
if (status) {
jniThrowRuntimeException(env, "Failed to unregister input channel. "
"Check logs for details.");
}
}
static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz,
jobject keyEventObj, jint nature, jint injectorPid, jint injectorUid,
jboolean sync, jint timeoutMillis) {
if (checkInputManagerUnitialized(env)) {
return INPUT_EVENT_INJECTION_FAILED;
}
KeyEvent keyEvent;
android_view_KeyEvent_toNative(env, keyEventObj, nature, & keyEvent);
return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent,
injectorPid, injectorUid, sync, timeoutMillis);
}
static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz,
jobject motionEventObj, jint nature, jint injectorPid, jint injectorUid,
jboolean sync, jint timeoutMillis) {
if (checkInputManagerUnitialized(env)) {
return INPUT_EVENT_INJECTION_FAILED;
}
MotionEvent motionEvent;
android_view_MotionEvent_toNative(env, motionEventObj, nature, & motionEvent);
return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent,
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[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V",
(void*) android_server_InputManager_nativeInit },
{ "nativeStart", "()V",
(void*) android_server_InputManager_nativeStart },
{ "nativeSetDisplaySize", "(III)V",
(void*) android_server_InputManager_nativeSetDisplaySize },
{ "nativeSetDisplayOrientation", "(II)V",
(void*) android_server_InputManager_nativeSetDisplayOrientation },
{ "nativeGetScanCodeState", "(III)I",
(void*) android_server_InputManager_nativeGetScanCodeState },
{ "nativeGetKeyCodeState", "(III)I",
(void*) android_server_InputManager_nativeGetKeyCodeState },
{ "nativeGetSwitchState", "(III)I",
(void*) android_server_InputManager_nativeGetSwitchState },
{ "nativeHasKeys", "([I[Z)Z",
(void*) android_server_InputManager_nativeHasKeys },
{ "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeRegisterInputChannel },
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
(void*) android_server_InputManager_nativeUnregisterInputChannel },
{ "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I",
(void*) android_server_InputManager_nativeInjectKeyEvent },
{ "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I",
(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) \
var = env->FindClass(className); \
LOG_FATAL_IF(! var, "Unable to find class " className); \
var = jclass(env->NewGlobalRef(var));
#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " methodName);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
int register_android_server_InputManager(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "com/android/server/InputManager",
gInputManagerMethods, NELEM(gInputManagerMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
// Callbacks
FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks");
GET_METHOD_ID(gCallbacksClassInfo.isScreenOn, gCallbacksClassInfo.clazz,
"isScreenOn", "()Z");
GET_METHOD_ID(gCallbacksClassInfo.isScreenBright, gCallbacksClassInfo.clazz,
"isScreenBright", "()Z");
GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz,
"notifyConfigurationChanged", "(JIII)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz,
"notifyLidSwitchChanged", "(JZ)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz,
"notifyInputChannelBroken", "(Landroid/view/InputChannel;)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz,
"notifyInputChannelANR", "(Landroid/view/InputChannel;)J");
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.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.pokeUserActivity, gCallbacksClassInfo.clazz,
"pokeUserActivity", "(JI)V");
GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz,
"notifyAppSwitchComing", "()V");
GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz,
"filterTouchEvents", "()Z");
GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz,
"filterJumpyTouchEvents", "()Z");
GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyDefinitions, gCallbacksClassInfo.clazz,
"getVirtualKeyDefinitions",
"(Ljava/lang/String;)[Lcom/android/server/InputManager$VirtualKeyDefinition;");
GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;");
// VirtualKeyDefinition
FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
"com/android/server/InputManager$VirtualKeyDefinition");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.scanCode, gVirtualKeyDefinitionClassInfo.clazz,
"scanCode", "I");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerX, gVirtualKeyDefinitionClassInfo.clazz,
"centerX", "I");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerY, gVirtualKeyDefinitionClassInfo.clazz,
"centerY", "I");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.width, gVirtualKeyDefinitionClassInfo.clazz,
"width", "I");
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
"height", "I");
// 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");
GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz,
"name", "Ljava/lang/String;");
GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos,
gInputApplicationClassInfo.clazz,
"dispatchingTimeoutNanos", "J");
GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz,
"token", "Ljava/lang/Object;");
return 0;
}
} /* namespace android */