More native input event dispatching.
Added ANRs handling. Added event injection. Fixed a NPE ActivityManagerServer writing ANRs to the drop box. Fixed HOME key interception. Fixed trackball reporting. Fixed pointer rotation in landscape mode. Change-Id: I50340f559f22899ab924e220a78119ffc79469b7
This commit is contained in:
@@ -341,11 +341,4 @@ public class MessageQueue {
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void poke()
|
||||
{
|
||||
synchronized (this) {
|
||||
nativeWake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import android.util.Slog;
|
||||
* to the ViewRoot through a Binder transaction as part of registering the Window.
|
||||
* @hide
|
||||
*/
|
||||
public class InputChannel implements Parcelable {
|
||||
public final class InputChannel implements Parcelable {
|
||||
private static final String TAG = "InputChannel";
|
||||
|
||||
public static final Parcelable.Creator<InputChannel> CREATOR
|
||||
|
||||
@@ -25,7 +25,7 @@ package android.view;
|
||||
* These parameters are used by the native input dispatching code.
|
||||
* @hide
|
||||
*/
|
||||
public class InputTarget {
|
||||
public final class InputTarget {
|
||||
public InputChannel mInputChannel;
|
||||
public int mFlags;
|
||||
public long mTimeoutNanos;
|
||||
|
||||
@@ -58,8 +58,6 @@ struct RawEvent {
|
||||
/*
|
||||
* Flags that flow alongside events in the input dispatch system to help with certain
|
||||
* policy decisions such as waking from device sleep.
|
||||
*
|
||||
* TODO This enumeration should probably be split up or relabeled for clarity.
|
||||
*/
|
||||
enum {
|
||||
/* These flags originate in RawEvents and are generally set in the key map. */
|
||||
@@ -73,6 +71,8 @@ enum {
|
||||
POLICY_FLAG_MENU = 0x00000040,
|
||||
POLICY_FLAG_LAUNCHER = 0x00000080,
|
||||
|
||||
POLICY_FLAG_RAW_MASK = 0x0000ffff,
|
||||
|
||||
/* These flags are set by the input reader policy as it intercepts each event. */
|
||||
|
||||
// Indicates that the screen was off when the event was received and the event
|
||||
|
||||
@@ -34,6 +34,28 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
/*
|
||||
* Constants used to report the outcome of input event injection.
|
||||
*/
|
||||
enum {
|
||||
/* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */
|
||||
INPUT_EVENT_INJECTION_PENDING = -1,
|
||||
|
||||
/* Injection succeeded. */
|
||||
INPUT_EVENT_INJECTION_SUCCEEDED = 0,
|
||||
|
||||
/* Injection failed because the injector did not have permission to inject
|
||||
* into the application with input focus. */
|
||||
INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1,
|
||||
|
||||
/* Injection failed because there were no available input targets. */
|
||||
INPUT_EVENT_INJECTION_FAILED = 2,
|
||||
|
||||
/* Injection failed due to a timeout. */
|
||||
INPUT_EVENT_INJECTION_TIMED_OUT = 3
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
@@ -70,6 +92,7 @@ struct InputTarget {
|
||||
float xOffset, yOffset;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Input dispatcher policy interface.
|
||||
*
|
||||
@@ -91,8 +114,11 @@ public:
|
||||
/* Notifies the system that an input channel is unrecoverably broken. */
|
||||
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) = 0;
|
||||
|
||||
/* Notifies the system that an input channel is not responding. */
|
||||
virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel) = 0;
|
||||
/* Notifies the system that an input channel is not responding.
|
||||
* Returns true and a new timeout value if the dispatcher should keep waiting.
|
||||
* Otherwise returns false. */
|
||||
virtual bool notifyInputChannelANR(const sp<InputChannel>& inputChannel,
|
||||
nsecs_t& outNewTimeout) = 0;
|
||||
|
||||
/* Notifies the system that an input channel recovered from ANR. */
|
||||
virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) = 0;
|
||||
@@ -100,12 +126,22 @@ 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. */
|
||||
virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
|
||||
/* Gets the input targets for a key event.
|
||||
* 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,
|
||||
int32_t injectorPid, int32_t injectorUid,
|
||||
Vector<InputTarget>& outTargets) = 0;
|
||||
|
||||
/* Gets the input targets for a motion event. */
|
||||
virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
|
||||
/* Gets the input targets for a motion event.
|
||||
* 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,
|
||||
int32_t injectorPid, int32_t injectorUid,
|
||||
Vector<InputTarget>& outTargets) = 0;
|
||||
};
|
||||
|
||||
@@ -139,6 +175,17 @@ public:
|
||||
uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
|
||||
float xPrecision, float yPrecision, nsecs_t downTime) = 0;
|
||||
|
||||
/* Injects an input event and optionally waits for sync.
|
||||
* This method may block even if sync is false because it must wait for previous events
|
||||
* to be dispatched before it can determine whether input event injection will be
|
||||
* permitted based on the current input focus.
|
||||
* Returns one of the INPUT_EVENT_INJECTION_XXX constants.
|
||||
*
|
||||
* This method may be called on any thread (usually by the input manager).
|
||||
*/
|
||||
virtual int32_t injectInputEvent(const InputEvent* event,
|
||||
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 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).
|
||||
@@ -183,6 +230,9 @@ public:
|
||||
uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords,
|
||||
float xPrecision, float yPrecision, nsecs_t downTime);
|
||||
|
||||
virtual int32_t injectInputEvent(const InputEvent* event,
|
||||
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
|
||||
|
||||
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
|
||||
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
|
||||
|
||||
@@ -205,7 +255,13 @@ private:
|
||||
int32_t type;
|
||||
nsecs_t eventTime;
|
||||
|
||||
int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING
|
||||
int32_t injectorPid; // -1 if not injected
|
||||
int32_t injectorUid; // -1 if not injected
|
||||
|
||||
bool dispatchInProgress; // initially false, set to true while dispatching
|
||||
|
||||
inline bool isInjected() { return injectorPid >= 0; }
|
||||
};
|
||||
|
||||
struct ConfigurationChangedEntry : EventEntry {
|
||||
@@ -293,6 +349,7 @@ private:
|
||||
struct CommandEntry;
|
||||
typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
|
||||
|
||||
class Connection;
|
||||
struct CommandEntry : Link<CommandEntry> {
|
||||
CommandEntry();
|
||||
~CommandEntry();
|
||||
@@ -300,7 +357,7 @@ private:
|
||||
Command command;
|
||||
|
||||
// parameters for the command (usage varies by command)
|
||||
sp<InputChannel> inputChannel;
|
||||
sp<Connection> connection;
|
||||
};
|
||||
|
||||
// Generic queue implementation.
|
||||
@@ -353,9 +410,16 @@ private:
|
||||
public:
|
||||
Allocator();
|
||||
|
||||
ConfigurationChangedEntry* obtainConfigurationChangedEntry();
|
||||
KeyEntry* obtainKeyEntry();
|
||||
MotionEntry* obtainMotionEntry();
|
||||
ConfigurationChangedEntry* obtainConfigurationChangedEntry(nsecs_t eventTime);
|
||||
KeyEntry* obtainKeyEntry(nsecs_t eventTime,
|
||||
int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
|
||||
int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
|
||||
int32_t repeatCount, nsecs_t downTime);
|
||||
MotionEntry* obtainMotionEntry(nsecs_t eventTime,
|
||||
int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
|
||||
int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
|
||||
nsecs_t downTime, uint32_t pointerCount,
|
||||
const int32_t* pointerIds, const PointerCoords* pointerCoords);
|
||||
DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry);
|
||||
CommandEntry* obtainCommandEntry(Command command);
|
||||
|
||||
@@ -367,7 +431,7 @@ private:
|
||||
void releaseCommandEntry(CommandEntry* entry);
|
||||
|
||||
void appendMotionSample(MotionEntry* motionEntry,
|
||||
nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords);
|
||||
nsecs_t eventTime, const PointerCoords* pointerCoords);
|
||||
|
||||
private:
|
||||
Pool<ConfigurationChangedEntry> mConfigurationChangeEntryPool;
|
||||
@@ -376,6 +440,8 @@ private:
|
||||
Pool<MotionSample> mMotionSamplePool;
|
||||
Pool<DispatchEntry> mDispatchEntryPool;
|
||||
Pool<CommandEntry> mCommandEntryPool;
|
||||
|
||||
void initializeEventEntry(EventEntry* entry, int32_t type, nsecs_t eventTime);
|
||||
};
|
||||
|
||||
/* Manages the dispatch state associated with a single input channel. */
|
||||
@@ -439,6 +505,8 @@ private:
|
||||
}
|
||||
|
||||
status_t initialize();
|
||||
|
||||
void setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout);
|
||||
};
|
||||
|
||||
sp<InputDispatcherPolicyInterface> mPolicy;
|
||||
@@ -455,8 +523,17 @@ private:
|
||||
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
|
||||
|
||||
// Active connections are connections that have a non-empty outbound queue.
|
||||
// We don't use a ref-counted pointer here because we explicitly abort connections
|
||||
// during unregistration which causes the connection's outbound queue to be cleared
|
||||
// and the connection itself to be deactivated.
|
||||
Vector<Connection*> mActiveConnections;
|
||||
|
||||
// List of connections that have timed out. Only used by dispatchOnce()
|
||||
// We don't use a ref-counted pointer here because it is not possible for a connection
|
||||
// to be unregistered while processing timed out connections since we hold the lock for
|
||||
// the duration.
|
||||
Vector<Connection*> mTimedOutConnections;
|
||||
|
||||
// Preallocated key and motion event objects used only to ask the input dispatcher policy
|
||||
// for the targets of an event that is to be dispatched.
|
||||
KeyEvent mReusableKeyEvent;
|
||||
@@ -468,6 +545,13 @@ private:
|
||||
Vector<InputTarget> mCurrentInputTargets;
|
||||
bool mCurrentInputTargetsValid; // false while targets are being recomputed
|
||||
|
||||
// Event injection and synchronization.
|
||||
Condition mInjectionResultAvailableCondition;
|
||||
Condition mFullySynchronizedCondition;
|
||||
bool isFullySynchronizedLocked();
|
||||
EventEntry* createEntryFromInputEventLocked(const InputEvent* event);
|
||||
void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
|
||||
|
||||
// Key repeat tracking.
|
||||
// XXX Move this up to the input reader instead.
|
||||
struct KeyRepeatState {
|
||||
@@ -500,13 +584,18 @@ private:
|
||||
nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample);
|
||||
|
||||
// Manage the dispatch cycle for a single connection.
|
||||
void prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
|
||||
// These methods are deliberately not Interruptible because doing all of the work
|
||||
// with the mutex held makes it easier to ensure that connection invariants are maintained.
|
||||
// If needed, the methods post commands to run later once the critical bits are done.
|
||||
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
||||
EventEntry* eventEntry, const InputTarget* inputTarget,
|
||||
bool resumeWithAppendedMotionSample);
|
||||
void startDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
|
||||
void finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
|
||||
bool timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection);
|
||||
bool abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
|
||||
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
|
||||
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
|
||||
void timeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
|
||||
void resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
|
||||
const sp<Connection>& connection, nsecs_t newTimeout);
|
||||
void abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
|
||||
bool broken);
|
||||
static bool handleReceiveCallback(int receiveFd, int events, void* data);
|
||||
|
||||
@@ -514,19 +603,17 @@ private:
|
||||
void activateConnectionLocked(Connection* connection);
|
||||
void deactivateConnectionLocked(Connection* connection);
|
||||
|
||||
// Outbound policy interactions.
|
||||
void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry);
|
||||
|
||||
// Interesting events that we might like to log or tell the framework about.
|
||||
void onDispatchCycleStartedLocked(
|
||||
nsecs_t currentTime, Connection* connection);
|
||||
nsecs_t currentTime, const sp<Connection>& connection);
|
||||
void onDispatchCycleFinishedLocked(
|
||||
nsecs_t currentTime, Connection* connection, bool recoveredFromANR);
|
||||
nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR);
|
||||
void onDispatchCycleANRLocked(
|
||||
nsecs_t currentTime, Connection* connection);
|
||||
nsecs_t currentTime, const sp<Connection>& connection);
|
||||
void onDispatchCycleBrokenLocked(
|
||||
nsecs_t currentTime, Connection* connection);
|
||||
nsecs_t currentTime, const sp<Connection>& connection);
|
||||
|
||||
// Outbound policy interactions.
|
||||
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
|
||||
void doNotifyInputChannelANRLockedInterruptible(CommandEntry* commandEntry);
|
||||
void doNotifyInputChannelRecoveredFromANRLockedInterruptible(CommandEntry* commandEntry);
|
||||
|
||||
@@ -78,6 +78,15 @@ public:
|
||||
/* Unregisters an input channel. */
|
||||
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0;
|
||||
|
||||
/* Injects an input event and optionally waits for sync.
|
||||
* This method may block even if sync is false because it must wait for previous events
|
||||
* to be dispatched before it can determine whether input event injection will be
|
||||
* permitted based on the current input focus.
|
||||
* Returns one of the INPUT_EVENT_INJECTION_XXX constants.
|
||||
*/
|
||||
virtual int32_t injectInputEvent(const InputEvent* event,
|
||||
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) = 0;
|
||||
|
||||
/* Gets input device configuration. */
|
||||
virtual void getInputConfiguration(InputConfiguration* outConfiguration) const = 0;
|
||||
|
||||
@@ -118,6 +127,9 @@ public:
|
||||
virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel);
|
||||
virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel);
|
||||
|
||||
virtual int32_t injectInputEvent(const InputEvent* event,
|
||||
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis);
|
||||
|
||||
virtual void getInputConfiguration(InputConfiguration* outConfiguration) const;
|
||||
virtual int32_t getScanCodeState(int32_t deviceId, int32_t deviceClasses,
|
||||
int32_t scanCode) const;
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
// Log debug messages about performance statistics.
|
||||
#define DEBUG_PERFORMANCE_STATISTICS 1
|
||||
|
||||
// Log debug messages about input event injection.
|
||||
#define DEBUG_INJECTION 1
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include <ui/InputDispatcher.h>
|
||||
|
||||
@@ -43,6 +46,10 @@ static inline bool isMovementKey(int32_t keyCode) {
|
||||
|| keyCode == KEYCODE_DPAD_RIGHT;
|
||||
}
|
||||
|
||||
static inline nsecs_t now() {
|
||||
return systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
}
|
||||
|
||||
// --- InputDispatcher ---
|
||||
|
||||
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
|
||||
@@ -84,7 +91,7 @@ void InputDispatcher::dispatchOnce() {
|
||||
nsecs_t nextWakeupTime = LONG_LONG_MAX;
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
currentTime = now();
|
||||
|
||||
// Reset the key repeat timer whenever we disallow key events, even if the next event
|
||||
// is not a key. This is to ensure that we abort a key repeat if the device is just coming
|
||||
@@ -94,33 +101,33 @@ void InputDispatcher::dispatchOnce() {
|
||||
resetKeyRepeatLocked();
|
||||
}
|
||||
|
||||
// Process timeouts for all connections and determine if there are any synchronous
|
||||
// event dispatches pending.
|
||||
// Detect and process timeouts for all connections and determine if there are any
|
||||
// synchronous event dispatches pending. This step is entirely non-interruptible.
|
||||
bool hasPendingSyncTarget = false;
|
||||
for (size_t i = 0; i < mActiveConnections.size(); ) {
|
||||
size_t activeConnectionCount = mActiveConnections.size();
|
||||
for (size_t i = 0; i < activeConnectionCount; i++) {
|
||||
Connection* connection = mActiveConnections.itemAt(i);
|
||||
|
||||
nsecs_t connectionTimeoutTime = connection->nextTimeoutTime;
|
||||
if (connectionTimeoutTime <= currentTime) {
|
||||
bool deactivated = timeoutDispatchCycleLocked(currentTime, connection);
|
||||
if (deactivated) {
|
||||
// Don't increment i because the connection has been removed
|
||||
// from mActiveConnections (hence, deactivated).
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (connectionTimeoutTime < nextWakeupTime) {
|
||||
nextWakeupTime = connectionTimeoutTime;
|
||||
}
|
||||
|
||||
if (connection->hasPendingSyncTarget()) {
|
||||
hasPendingSyncTarget = true;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
nsecs_t connectionTimeoutTime = connection->nextTimeoutTime;
|
||||
if (connectionTimeoutTime <= currentTime) {
|
||||
mTimedOutConnections.add(connection);
|
||||
} else if (connectionTimeoutTime < nextWakeupTime) {
|
||||
nextWakeupTime = connectionTimeoutTime;
|
||||
}
|
||||
}
|
||||
|
||||
size_t timedOutConnectionCount = mTimedOutConnections.size();
|
||||
for (size_t i = 0; i < timedOutConnectionCount; i++) {
|
||||
Connection* connection = mTimedOutConnections.itemAt(i);
|
||||
timeoutDispatchCycleLocked(currentTime, connection);
|
||||
skipPoll = true;
|
||||
}
|
||||
mTimedOutConnections.clear();
|
||||
|
||||
// If we don't have a pending sync target, then we can begin delivering a new event.
|
||||
// (Otherwise we wait for dispatch to complete for that target.)
|
||||
if (! hasPendingSyncTarget) {
|
||||
@@ -177,6 +184,11 @@ void InputDispatcher::dispatchOnce() {
|
||||
|
||||
// Run any deferred commands.
|
||||
skipPoll |= runCommandsLockedInterruptible();
|
||||
|
||||
// Wake up synchronization waiters, if needed.
|
||||
if (isFullySynchronizedLocked()) {
|
||||
mFullySynchronizedCondition.broadcast();
|
||||
}
|
||||
} // release lock
|
||||
|
||||
// If we dispatched anything, don't poll just now. Wait for the next iteration.
|
||||
@@ -202,6 +214,7 @@ bool InputDispatcher::runCommandsLockedInterruptible() {
|
||||
Command command = commandEntry->command;
|
||||
(this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
|
||||
|
||||
commandEntry->connection.clear();
|
||||
mAllocator.releaseCommandEntry(commandEntry);
|
||||
} while (! mCommandQueue.isEmpty());
|
||||
return true;
|
||||
@@ -272,28 +285,23 @@ void InputDispatcher::processKeyRepeatLockedInterruptible(
|
||||
// Synthesize a key repeat after the repeat timeout expired.
|
||||
// We reuse the previous key entry if otherwise unreferenced.
|
||||
KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
|
||||
uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK;
|
||||
if (entry->refCount == 1) {
|
||||
entry->eventTime = currentTime;
|
||||
entry->downTime = currentTime;
|
||||
entry->policyFlags = policyFlags;
|
||||
entry->repeatCount += 1;
|
||||
} else {
|
||||
KeyEntry* newEntry = mAllocator.obtainKeyEntry();
|
||||
newEntry->deviceId = entry->deviceId;
|
||||
newEntry->nature = entry->nature;
|
||||
newEntry->policyFlags = entry->policyFlags;
|
||||
newEntry->action = entry->action;
|
||||
newEntry->flags = entry->flags;
|
||||
newEntry->keyCode = entry->keyCode;
|
||||
newEntry->scanCode = entry->scanCode;
|
||||
newEntry->metaState = entry->metaState;
|
||||
newEntry->repeatCount = entry->repeatCount + 1;
|
||||
KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime,
|
||||
entry->deviceId, entry->nature, policyFlags,
|
||||
entry->action, entry->flags, entry->keyCode, entry->scanCode,
|
||||
entry->metaState, entry->repeatCount + 1, currentTime);
|
||||
|
||||
mKeyRepeatState.lastKeyEntry = newEntry;
|
||||
mAllocator.releaseKeyEntry(entry);
|
||||
|
||||
entry = newEntry;
|
||||
}
|
||||
entry->eventTime = currentTime;
|
||||
entry->downTime = currentTime;
|
||||
entry->policyFlags = 0;
|
||||
|
||||
mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatTimeout;
|
||||
|
||||
@@ -358,12 +366,15 @@ void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible(
|
||||
entry->downTime, entry->eventTime);
|
||||
|
||||
mCurrentInputTargets.clear();
|
||||
mPolicy->getKeyEventTargets(& mReusableKeyEvent, entry->policyFlags,
|
||||
int32_t injectionResult = mPolicy->getKeyEventTargets(& mReusableKeyEvent,
|
||||
entry->policyFlags, entry->injectorPid, entry->injectorUid,
|
||||
mCurrentInputTargets);
|
||||
|
||||
mLock.lock();
|
||||
mCurrentInputTargetsValid = true;
|
||||
|
||||
setInjectionResultLocked(entry, injectionResult);
|
||||
|
||||
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
|
||||
}
|
||||
|
||||
@@ -384,12 +395,15 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible(
|
||||
entry->firstSample.pointerCoords);
|
||||
|
||||
mCurrentInputTargets.clear();
|
||||
mPolicy->getMotionEventTargets(& mReusableMotionEvent, entry->policyFlags,
|
||||
int32_t injectionResult = mPolicy->getMotionEventTargets(& mReusableMotionEvent,
|
||||
entry->policyFlags, entry->injectorPid, entry->injectorUid,
|
||||
mCurrentInputTargets);
|
||||
|
||||
mLock.lock();
|
||||
mCurrentInputTargetsValid = true;
|
||||
|
||||
setInjectionResultLocked(entry, injectionResult);
|
||||
|
||||
dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false);
|
||||
}
|
||||
|
||||
@@ -410,7 +424,7 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi
|
||||
inputTarget.inputChannel->getReceivePipeFd());
|
||||
if (connectionIndex >= 0) {
|
||||
sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);
|
||||
prepareDispatchCycleLocked(currentTime, connection.get(), eventEntry, & inputTarget,
|
||||
prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,
|
||||
resumeWithAppendedMotionSample);
|
||||
} else {
|
||||
LOGW("Framework requested delivery of an input event to channel '%s' but it "
|
||||
@@ -420,8 +434,8 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi
|
||||
}
|
||||
}
|
||||
|
||||
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
|
||||
EventEntry* eventEntry, const InputTarget* inputTarget,
|
||||
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
|
||||
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
|
||||
bool resumeWithAppendedMotionSample) {
|
||||
#if DEBUG_DISPATCH_CYCLE
|
||||
LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, timeout=%lldns, "
|
||||
@@ -547,12 +561,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, Connection
|
||||
|
||||
// If the outbound queue was previously empty, start the dispatch cycle going.
|
||||
if (wasEmpty) {
|
||||
activateConnectionLocked(connection);
|
||||
activateConnectionLocked(connection.get());
|
||||
startDispatchCycleLocked(currentTime, connection);
|
||||
}
|
||||
}
|
||||
|
||||
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
|
||||
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
|
||||
const sp<Connection>& connection) {
|
||||
#if DEBUG_DISPATCH_CYCLE
|
||||
LOGD("channel '%s' ~ startDispatchCycle",
|
||||
connection->getInputChannelName());
|
||||
@@ -682,13 +697,14 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, Connection*
|
||||
connection->lastDispatchTime = currentTime;
|
||||
|
||||
nsecs_t timeout = dispatchEntry->timeout;
|
||||
connection->nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
|
||||
connection->setNextTimeoutTime(currentTime, timeout);
|
||||
|
||||
// Notify other system components.
|
||||
onDispatchCycleStartedLocked(currentTime, connection);
|
||||
}
|
||||
|
||||
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
|
||||
void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
|
||||
const sp<Connection>& connection) {
|
||||
#if DEBUG_DISPATCH_CYCLE
|
||||
LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, "
|
||||
"%01.1fms since dispatch",
|
||||
@@ -756,31 +772,48 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, Connection*
|
||||
}
|
||||
|
||||
// Outbound queue is empty, deactivate the connection.
|
||||
deactivateConnectionLocked(connection);
|
||||
deactivateConnectionLocked(connection.get());
|
||||
}
|
||||
|
||||
bool InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, Connection* connection) {
|
||||
void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime,
|
||||
const sp<Connection>& connection) {
|
||||
#if DEBUG_DISPATCH_CYCLE
|
||||
LOGD("channel '%s' ~ timeoutDispatchCycle",
|
||||
connection->getInputChannelName());
|
||||
#endif
|
||||
|
||||
if (connection->status != Connection::STATUS_NORMAL) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter the not responding state.
|
||||
connection->status = Connection::STATUS_NOT_RESPONDING;
|
||||
connection->lastANRTime = currentTime;
|
||||
bool deactivated = abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
|
||||
|
||||
// Notify other system components.
|
||||
// This enqueues a command which will eventually either call
|
||||
// resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked.
|
||||
onDispatchCycleANRLocked(currentTime, connection);
|
||||
return deactivated;
|
||||
}
|
||||
|
||||
bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection* connection,
|
||||
bool broken) {
|
||||
void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime,
|
||||
const sp<Connection>& connection, nsecs_t newTimeout) {
|
||||
#if DEBUG_DISPATCH_CYCLE
|
||||
LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked",
|
||||
connection->getInputChannelName());
|
||||
#endif
|
||||
|
||||
if (connection->status != Connection::STATUS_NOT_RESPONDING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resume normal dispatch.
|
||||
connection->status = Connection::STATUS_NORMAL;
|
||||
connection->setNextTimeoutTime(currentTime, newTimeout);
|
||||
}
|
||||
|
||||
void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime,
|
||||
const sp<Connection>& connection, bool broken) {
|
||||
#if DEBUG_DISPATCH_CYCLE
|
||||
LOGD("channel '%s' ~ abortDispatchCycle - broken=%s",
|
||||
connection->getInputChannelName(), broken ? "true" : "false");
|
||||
@@ -790,14 +823,13 @@ bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection*
|
||||
connection->nextTimeoutTime = LONG_LONG_MAX;
|
||||
|
||||
// Clear the outbound queue.
|
||||
bool deactivated = ! connection->outboundQueue.isEmpty();
|
||||
if (deactivated) {
|
||||
if (! connection->outboundQueue.isEmpty()) {
|
||||
do {
|
||||
DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead();
|
||||
mAllocator.releaseDispatchEntry(dispatchEntry);
|
||||
} while (! connection->outboundQueue.isEmpty());
|
||||
|
||||
deactivateConnectionLocked(connection);
|
||||
deactivateConnectionLocked(connection.get());
|
||||
}
|
||||
|
||||
// Handle the case where the connection appears to be unrecoverably broken.
|
||||
@@ -811,8 +843,6 @@ bool InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, Connection*
|
||||
onDispatchCycleBrokenLocked(currentTime, connection);
|
||||
}
|
||||
}
|
||||
|
||||
return deactivated;
|
||||
}
|
||||
|
||||
bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) {
|
||||
@@ -828,13 +858,13 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat
|
||||
return false; // remove the callback
|
||||
}
|
||||
|
||||
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
nsecs_t currentTime = now();
|
||||
|
||||
sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex);
|
||||
if (events & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||
LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. "
|
||||
"events=0x%x", connection->getInputChannelName(), events);
|
||||
d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
|
||||
d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
|
||||
d->runCommandsLockedInterruptible();
|
||||
return false; // remove the callback
|
||||
}
|
||||
@@ -849,12 +879,12 @@ bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* dat
|
||||
if (status) {
|
||||
LOGE("channel '%s' ~ Failed to receive finished signal. status=%d",
|
||||
connection->getInputChannelName(), status);
|
||||
d->abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
|
||||
d->abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
|
||||
d->runCommandsLockedInterruptible();
|
||||
return false; // remove the callback
|
||||
}
|
||||
|
||||
d->finishDispatchCycleLocked(currentTime, connection.get());
|
||||
d->finishDispatchCycleLocked(currentTime, connection);
|
||||
d->runCommandsLockedInterruptible();
|
||||
return true;
|
||||
} // release lock
|
||||
@@ -869,8 +899,7 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) {
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry();
|
||||
newEntry->eventTime = eventTime;
|
||||
ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime);
|
||||
|
||||
wasEmpty = mInboundQueue.isEmpty();
|
||||
mInboundQueue.enqueueAtTail(newEntry);
|
||||
@@ -902,6 +931,9 @@ void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) {
|
||||
LOGV("Dropping movement key during app switch: keyCode=%d, action=%d",
|
||||
keyEntry->keyCode, keyEntry->action);
|
||||
mInboundQueue.dequeue(keyEntry);
|
||||
|
||||
setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
|
||||
|
||||
mAllocator.releaseKeyEntry(keyEntry);
|
||||
} else {
|
||||
// stop at last non-movement key
|
||||
@@ -928,18 +960,10 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t nat
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
KeyEntry* newEntry = mAllocator.obtainKeyEntry();
|
||||
newEntry->eventTime = eventTime;
|
||||
newEntry->deviceId = deviceId;
|
||||
newEntry->nature = nature;
|
||||
newEntry->policyFlags = policyFlags;
|
||||
newEntry->action = action;
|
||||
newEntry->flags = flags;
|
||||
newEntry->keyCode = keyCode;
|
||||
newEntry->scanCode = scanCode;
|
||||
newEntry->metaState = metaState;
|
||||
newEntry->repeatCount = 0;
|
||||
newEntry->downTime = downTime;
|
||||
int32_t repeatCount = 0;
|
||||
KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime,
|
||||
deviceId, nature, policyFlags, action, flags, keyCode, scanCode,
|
||||
metaState, repeatCount, downTime);
|
||||
|
||||
wasEmpty = mInboundQueue.isEmpty();
|
||||
mInboundQueue.enqueueAtTail(newEntry);
|
||||
@@ -992,7 +1016,8 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
|
||||
}
|
||||
|
||||
if (motionEntry->action != MOTION_EVENT_ACTION_MOVE
|
||||
|| motionEntry->pointerCount != pointerCount) {
|
||||
|| motionEntry->pointerCount != pointerCount
|
||||
|| motionEntry->isInjected()) {
|
||||
// Last motion event in the queue for this device is not compatible for
|
||||
// appending new samples. Stop here.
|
||||
goto NoBatchingOrStreaming;
|
||||
@@ -1000,7 +1025,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
|
||||
|
||||
// The last motion event is a move and is compatible for appending.
|
||||
// Do the batching magic.
|
||||
mAllocator.appendMotionSample(motionEntry, eventTime, pointerCount, pointerCoords);
|
||||
mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords);
|
||||
#if DEBUG_BATCHING
|
||||
LOGD("Appended motion sample onto batch for most recent "
|
||||
"motion event for this device in the inbound queue.");
|
||||
@@ -1053,18 +1078,19 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t
|
||||
dispatchEntry->eventEntry);
|
||||
if (syncedMotionEntry->action != MOTION_EVENT_ACTION_MOVE
|
||||
|| syncedMotionEntry->deviceId != deviceId
|
||||
|| syncedMotionEntry->pointerCount != pointerCount) {
|
||||
|| syncedMotionEntry->pointerCount != pointerCount
|
||||
|| syncedMotionEntry->isInjected()) {
|
||||
goto NoBatchingOrStreaming;
|
||||
}
|
||||
|
||||
// Found synced move entry. Append sample and resume dispatch.
|
||||
mAllocator.appendMotionSample(syncedMotionEntry, eventTime,
|
||||
pointerCount, pointerCoords);
|
||||
pointerCoords);
|
||||
#if DEBUG_BATCHING
|
||||
LOGD("Appended motion sample onto batch for most recent synchronously "
|
||||
"dispatched motion event for this device in the outbound queues.");
|
||||
#endif
|
||||
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
nsecs_t currentTime = now();
|
||||
dispatchEventToCurrentInputTargetsLocked(currentTime, syncedMotionEntry,
|
||||
true /*resumeWithAppendedMotionSample*/);
|
||||
|
||||
@@ -1079,24 +1105,10 @@ NoBatchingOrStreaming:;
|
||||
}
|
||||
|
||||
// Just enqueue a new motion event.
|
||||
MotionEntry* newEntry = mAllocator.obtainMotionEntry();
|
||||
newEntry->eventTime = eventTime;
|
||||
newEntry->deviceId = deviceId;
|
||||
newEntry->nature = nature;
|
||||
newEntry->policyFlags = policyFlags;
|
||||
newEntry->action = action;
|
||||
newEntry->metaState = metaState;
|
||||
newEntry->edgeFlags = edgeFlags;
|
||||
newEntry->xPrecision = xPrecision;
|
||||
newEntry->yPrecision = yPrecision;
|
||||
newEntry->downTime = downTime;
|
||||
newEntry->pointerCount = pointerCount;
|
||||
newEntry->firstSample.eventTime = eventTime;
|
||||
newEntry->lastSample = & newEntry->firstSample;
|
||||
for (uint32_t i = 0; i < pointerCount; i++) {
|
||||
newEntry->pointerIds[i] = pointerIds[i];
|
||||
newEntry->firstSample.pointerCoords[i] = pointerCoords[i];
|
||||
}
|
||||
MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime,
|
||||
deviceId, nature, policyFlags, action, metaState, edgeFlags,
|
||||
xPrecision, yPrecision, downTime,
|
||||
pointerCount, pointerIds, pointerCoords);
|
||||
|
||||
wasEmpty = mInboundQueue.isEmpty();
|
||||
mInboundQueue.enqueueAtTail(newEntry);
|
||||
@@ -1107,6 +1119,133 @@ NoBatchingOrStreaming:;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t InputDispatcher::injectInputEvent(const InputEvent* event,
|
||||
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
|
||||
#if DEBUG_INBOUND_EVENT_DETAILS
|
||||
LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
|
||||
"sync=%d, timeoutMillis=%d",
|
||||
event->getType(), injectorPid, injectorUid, sync, timeoutMillis);
|
||||
#endif
|
||||
|
||||
nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
|
||||
|
||||
EventEntry* injectedEntry;
|
||||
bool wasEmpty;
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
injectedEntry = createEntryFromInputEventLocked(event);
|
||||
injectedEntry->refCount += 1;
|
||||
injectedEntry->injectorPid = injectorPid;
|
||||
injectedEntry->injectorUid = injectorUid;
|
||||
|
||||
wasEmpty = mInboundQueue.isEmpty();
|
||||
mInboundQueue.enqueueAtTail(injectedEntry);
|
||||
|
||||
} // release lock
|
||||
|
||||
if (wasEmpty) {
|
||||
mPollLoop->wake();
|
||||
}
|
||||
|
||||
int32_t injectionResult;
|
||||
{ // acquire lock
|
||||
AutoMutex _l(mLock);
|
||||
|
||||
for (;;) {
|
||||
injectionResult = injectedEntry->injectionResult;
|
||||
if (injectionResult != INPUT_EVENT_INJECTION_PENDING) {
|
||||
break;
|
||||
}
|
||||
|
||||
nsecs_t remainingTimeout = endTime - now();
|
||||
if (remainingTimeout <= 0) {
|
||||
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
|
||||
sync = false;
|
||||
break;
|
||||
}
|
||||
|
||||
mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
while (! isFullySynchronizedLocked()) {
|
||||
nsecs_t remainingTimeout = endTime - now();
|
||||
if (remainingTimeout <= 0) {
|
||||
injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT;
|
||||
break;
|
||||
}
|
||||
|
||||
mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
mAllocator.releaseEventEntry(injectedEntry);
|
||||
} // release lock
|
||||
|
||||
return injectionResult;
|
||||
}
|
||||
|
||||
void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
|
||||
if (entry->isInjected()) {
|
||||
#if DEBUG_INJECTION
|
||||
LOGD("Setting input event injection result to %d. "
|
||||
"injectorPid=%d, injectorUid=%d",
|
||||
injectionResult, entry->injectorPid, entry->injectorUid);
|
||||
#endif
|
||||
|
||||
entry->injectionResult = injectionResult;
|
||||
mInjectionResultAvailableCondition.broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
bool InputDispatcher::isFullySynchronizedLocked() {
|
||||
return mInboundQueue.isEmpty() && mActiveConnections.isEmpty();
|
||||
}
|
||||
|
||||
InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked(
|
||||
const InputEvent* event) {
|
||||
switch (event->getType()) {
|
||||
case INPUT_EVENT_TYPE_KEY: {
|
||||
const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event);
|
||||
uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
|
||||
|
||||
KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(),
|
||||
keyEvent->getDeviceId(), keyEvent->getNature(), policyFlags,
|
||||
keyEvent->getAction(), keyEvent->getFlags(),
|
||||
keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
|
||||
keyEvent->getRepeatCount(), keyEvent->getDownTime());
|
||||
return keyEntry;
|
||||
}
|
||||
|
||||
case INPUT_EVENT_TYPE_MOTION: {
|
||||
const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event);
|
||||
uint32_t policyFlags = 0; // XXX consider adding a policy flag to track injected events
|
||||
|
||||
const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
|
||||
const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
|
||||
size_t pointerCount = motionEvent->getPointerCount();
|
||||
|
||||
MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes,
|
||||
motionEvent->getDeviceId(), motionEvent->getNature(), policyFlags,
|
||||
motionEvent->getAction(), motionEvent->getMetaState(), motionEvent->getEdgeFlags(),
|
||||
motionEvent->getXPrecision(), motionEvent->getYPrecision(),
|
||||
motionEvent->getDownTime(), uint32_t(pointerCount),
|
||||
motionEvent->getPointerIds(), samplePointerCoords);
|
||||
for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
|
||||
sampleEventTimes += 1;
|
||||
samplePointerCoords += pointerCount;
|
||||
mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords);
|
||||
}
|
||||
return motionEntry;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void InputDispatcher::resetKeyRepeatLocked() {
|
||||
if (mKeyRepeatState.lastKeyEntry) {
|
||||
mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry);
|
||||
@@ -1169,8 +1308,8 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh
|
||||
|
||||
connection->status = Connection::STATUS_ZOMBIE;
|
||||
|
||||
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
abortDispatchCycleLocked(currentTime, connection.get(), true /*broken*/);
|
||||
nsecs_t currentTime = now();
|
||||
abortDispatchCycleLocked(currentTime, connection, true /*broken*/);
|
||||
|
||||
runCommandsLockedInterruptible();
|
||||
} // release lock
|
||||
@@ -1202,11 +1341,11 @@ void InputDispatcher::deactivateConnectionLocked(Connection* connection) {
|
||||
}
|
||||
|
||||
void InputDispatcher::onDispatchCycleStartedLocked(
|
||||
nsecs_t currentTime, Connection* connection) {
|
||||
nsecs_t currentTime, const sp<Connection>& connection) {
|
||||
}
|
||||
|
||||
void InputDispatcher::onDispatchCycleFinishedLocked(
|
||||
nsecs_t currentTime, Connection* connection, bool recoveredFromANR) {
|
||||
nsecs_t currentTime, const sp<Connection>& connection, bool recoveredFromANR) {
|
||||
if (recoveredFromANR) {
|
||||
LOGI("channel '%s' ~ Recovered from ANR. %01.1fms since event, "
|
||||
"%01.1fms since dispatch, %01.1fms since ANR",
|
||||
@@ -1217,12 +1356,12 @@ void InputDispatcher::onDispatchCycleFinishedLocked(
|
||||
|
||||
CommandEntry* commandEntry = postCommandLocked(
|
||||
& InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible);
|
||||
commandEntry->inputChannel = connection->inputChannel;
|
||||
commandEntry->connection = connection;
|
||||
}
|
||||
}
|
||||
|
||||
void InputDispatcher::onDispatchCycleANRLocked(
|
||||
nsecs_t currentTime, Connection* connection) {
|
||||
nsecs_t currentTime, const sp<Connection>& connection) {
|
||||
LOGI("channel '%s' ~ Not responding! %01.1fms since event, %01.1fms since dispatch",
|
||||
connection->getInputChannelName(),
|
||||
connection->getEventLatencyMillis(currentTime),
|
||||
@@ -1230,47 +1369,64 @@ void InputDispatcher::onDispatchCycleANRLocked(
|
||||
|
||||
CommandEntry* commandEntry = postCommandLocked(
|
||||
& InputDispatcher::doNotifyInputChannelANRLockedInterruptible);
|
||||
commandEntry->inputChannel = connection->inputChannel;
|
||||
commandEntry->connection = connection;
|
||||
}
|
||||
|
||||
void InputDispatcher::onDispatchCycleBrokenLocked(
|
||||
nsecs_t currentTime, Connection* connection) {
|
||||
nsecs_t currentTime, const sp<Connection>& connection) {
|
||||
LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
|
||||
connection->getInputChannelName());
|
||||
|
||||
CommandEntry* commandEntry = postCommandLocked(
|
||||
& InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
|
||||
commandEntry->inputChannel = connection->inputChannel;
|
||||
commandEntry->connection = connection;
|
||||
}
|
||||
|
||||
void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
|
||||
CommandEntry* commandEntry) {
|
||||
mLock.unlock();
|
||||
sp<Connection> connection = commandEntry->connection;
|
||||
|
||||
mPolicy->notifyInputChannelBroken(commandEntry->inputChannel);
|
||||
commandEntry->inputChannel.clear();
|
||||
if (connection->status != Connection::STATUS_ZOMBIE) {
|
||||
mLock.unlock();
|
||||
|
||||
mLock.lock();
|
||||
mPolicy->notifyInputChannelBroken(connection->inputChannel);
|
||||
|
||||
mLock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
void InputDispatcher::doNotifyInputChannelANRLockedInterruptible(
|
||||
CommandEntry* commandEntry) {
|
||||
mLock.unlock();
|
||||
sp<Connection> connection = commandEntry->connection;
|
||||
|
||||
mPolicy->notifyInputChannelANR(commandEntry->inputChannel);
|
||||
commandEntry->inputChannel.clear();
|
||||
if (connection->status != Connection::STATUS_ZOMBIE) {
|
||||
mLock.unlock();
|
||||
|
||||
mLock.lock();
|
||||
nsecs_t newTimeout;
|
||||
bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout);
|
||||
|
||||
mLock.lock();
|
||||
|
||||
nsecs_t currentTime = now();
|
||||
if (resume) {
|
||||
resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout);
|
||||
} else {
|
||||
abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
|
||||
CommandEntry* commandEntry) {
|
||||
mLock.unlock();
|
||||
sp<Connection> connection = commandEntry->connection;
|
||||
|
||||
mPolicy->notifyInputChannelRecoveredFromANR(commandEntry->inputChannel);
|
||||
commandEntry->inputChannel.clear();
|
||||
if (connection->status != Connection::STATUS_ZOMBIE) {
|
||||
mLock.unlock();
|
||||
|
||||
mLock.lock();
|
||||
mPolicy->notifyInputChannelRecoveredFromANR(connection->inputChannel);
|
||||
|
||||
mLock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1279,29 +1435,69 @@ void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible(
|
||||
InputDispatcher::Allocator::Allocator() {
|
||||
}
|
||||
|
||||
void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type,
|
||||
nsecs_t eventTime) {
|
||||
entry->type = type;
|
||||
entry->refCount = 1;
|
||||
entry->dispatchInProgress = false;
|
||||
entry->injectionResult = INPUT_EVENT_INJECTION_PENDING;
|
||||
entry->injectorPid = -1;
|
||||
entry->injectorUid = -1;
|
||||
}
|
||||
|
||||
InputDispatcher::ConfigurationChangedEntry*
|
||||
InputDispatcher::Allocator::obtainConfigurationChangedEntry() {
|
||||
InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) {
|
||||
ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc();
|
||||
entry->refCount = 1;
|
||||
entry->type = EventEntry::TYPE_CONFIGURATION_CHANGED;
|
||||
entry->dispatchInProgress = false;
|
||||
initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime);
|
||||
return entry;
|
||||
}
|
||||
|
||||
InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry() {
|
||||
InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime,
|
||||
int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
|
||||
int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
|
||||
int32_t repeatCount, nsecs_t downTime) {
|
||||
KeyEntry* entry = mKeyEntryPool.alloc();
|
||||
entry->refCount = 1;
|
||||
entry->type = EventEntry::TYPE_KEY;
|
||||
entry->dispatchInProgress = false;
|
||||
initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime);
|
||||
|
||||
entry->deviceId = deviceId;
|
||||
entry->nature = nature;
|
||||
entry->policyFlags = policyFlags;
|
||||
entry->action = action;
|
||||
entry->flags = flags;
|
||||
entry->keyCode = keyCode;
|
||||
entry->scanCode = scanCode;
|
||||
entry->metaState = metaState;
|
||||
entry->repeatCount = repeatCount;
|
||||
entry->downTime = downTime;
|
||||
return entry;
|
||||
}
|
||||
|
||||
InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry() {
|
||||
InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime,
|
||||
int32_t deviceId, int32_t nature, uint32_t policyFlags, int32_t action,
|
||||
int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision,
|
||||
nsecs_t downTime, uint32_t pointerCount,
|
||||
const int32_t* pointerIds, const PointerCoords* pointerCoords) {
|
||||
MotionEntry* entry = mMotionEntryPool.alloc();
|
||||
entry->refCount = 1;
|
||||
entry->type = EventEntry::TYPE_MOTION;
|
||||
initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime);
|
||||
|
||||
entry->eventTime = eventTime;
|
||||
entry->deviceId = deviceId;
|
||||
entry->nature = nature;
|
||||
entry->policyFlags = policyFlags;
|
||||
entry->action = action;
|
||||
entry->metaState = metaState;
|
||||
entry->edgeFlags = edgeFlags;
|
||||
entry->xPrecision = xPrecision;
|
||||
entry->yPrecision = yPrecision;
|
||||
entry->downTime = downTime;
|
||||
entry->pointerCount = pointerCount;
|
||||
entry->firstSample.eventTime = eventTime;
|
||||
entry->firstSample.next = NULL;
|
||||
entry->dispatchInProgress = false;
|
||||
entry->lastSample = & entry->firstSample;
|
||||
for (uint32_t i = 0; i < pointerCount; i++) {
|
||||
entry->pointerIds[i] = pointerIds[i];
|
||||
entry->firstSample.pointerCoords[i] = pointerCoords[i];
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@@ -1379,10 +1575,11 @@ void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) {
|
||||
}
|
||||
|
||||
void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
|
||||
nsecs_t eventTime, int32_t pointerCount, const PointerCoords* pointerCoords) {
|
||||
nsecs_t eventTime, const PointerCoords* pointerCoords) {
|
||||
MotionSample* sample = mMotionSamplePool.alloc();
|
||||
sample->eventTime = eventTime;
|
||||
for (int32_t i = 0; i < pointerCount; i++) {
|
||||
uint32_t pointerCount = motionEntry->pointerCount;
|
||||
for (uint32_t i = 0; i < pointerCount; i++) {
|
||||
sample->pointerCoords[i] = pointerCoords[i];
|
||||
}
|
||||
|
||||
@@ -1407,6 +1604,10 @@ status_t InputDispatcher::Connection::initialize() {
|
||||
return inputPublisher.initialize();
|
||||
}
|
||||
|
||||
void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_t timeout) {
|
||||
nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX;
|
||||
}
|
||||
|
||||
const char* InputDispatcher::Connection::getStatusLabel() const {
|
||||
switch (status) {
|
||||
case STATUS_NORMAL:
|
||||
|
||||
@@ -80,6 +80,11 @@ status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChann
|
||||
return mDispatcher->unregisterInputChannel(inputChannel);
|
||||
}
|
||||
|
||||
int32_t InputManager::injectInputEvent(const InputEvent* event,
|
||||
int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) {
|
||||
return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, sync, timeoutMillis);
|
||||
}
|
||||
|
||||
void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) const {
|
||||
mReader->getCurrentInputConfiguration(outConfiguration);
|
||||
}
|
||||
|
||||
@@ -1444,7 +1444,7 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli
|
||||
case InputReaderPolicyInterface::ROTATION_90: {
|
||||
float xTemp = x;
|
||||
x = y;
|
||||
y = mDisplayHeight - xTemp;
|
||||
y = mDisplayWidth - xTemp;
|
||||
break;
|
||||
}
|
||||
case InputReaderPolicyInterface::ROTATION_180: {
|
||||
@@ -1454,7 +1454,7 @@ void InputReader::dispatchTouch(nsecs_t when, InputDevice* device, uint32_t poli
|
||||
}
|
||||
case InputReaderPolicyInterface::ROTATION_270: {
|
||||
float xTemp = x;
|
||||
x = mDisplayWidth - y;
|
||||
x = mDisplayHeight - y;
|
||||
y = xTemp;
|
||||
break;
|
||||
}
|
||||
@@ -1510,7 +1510,7 @@ void InputReader::onTrackballStateChanged(nsecs_t when,
|
||||
|
||||
uint32_t fields = device->trackball.accumulator.fields;
|
||||
bool downChanged = fields & InputDevice::TrackballState::Accumulator::FIELD_BTN_MOUSE;
|
||||
bool deltaChanged = (fields & DELTA_FIELDS) == DELTA_FIELDS;
|
||||
bool deltaChanged = fields & DELTA_FIELDS;
|
||||
|
||||
bool down;
|
||||
if (downChanged) {
|
||||
@@ -1546,10 +1546,10 @@ void InputReader::onTrackballStateChanged(nsecs_t when,
|
||||
|
||||
int32_t pointerId = 0;
|
||||
PointerCoords pointerCoords;
|
||||
pointerCoords.x = device->trackball.accumulator.relX
|
||||
* device->trackball.precalculated.xScale;
|
||||
pointerCoords.y = device->trackball.accumulator.relY
|
||||
* device->trackball.precalculated.yScale;
|
||||
pointerCoords.x = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_X
|
||||
? device->trackball.accumulator.relX * device->trackball.precalculated.xScale : 0;
|
||||
pointerCoords.y = fields & InputDevice::TrackballState::Accumulator::FIELD_REL_Y
|
||||
? device->trackball.accumulator.relY * device->trackball.precalculated.yScale : 0;
|
||||
pointerCoords.pressure = 1.0f; // XXX Consider making this 1.0f if down, 0 otherwise.
|
||||
pointerCoords.size = 0;
|
||||
|
||||
|
||||
@@ -81,6 +81,10 @@ public class InputManager {
|
||||
private static native boolean nativeHasKeys(int[] keyCodes, boolean[] keyExists);
|
||||
private static native void nativeRegisterInputChannel(InputChannel inputChannel);
|
||||
private static native void nativeUnregisterInputChannel(InputChannel inputChannel);
|
||||
private static native int nativeInjectKeyEvent(KeyEvent event, int nature,
|
||||
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);
|
||||
|
||||
// Device class as defined by EventHub.
|
||||
private static final int CLASS_KEYBOARD = 0x00000001;
|
||||
@@ -90,6 +94,12 @@ public class InputManager {
|
||||
private static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
|
||||
private static final int CLASS_DPAD = 0x00000020;
|
||||
|
||||
// Input event injection constants defined in InputDispatcher.h.
|
||||
static final int INPUT_EVENT_INJECTION_SUCCEEDED = 0;
|
||||
static final int INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1;
|
||||
static final int INPUT_EVENT_INJECTION_FAILED = 2;
|
||||
static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3;
|
||||
|
||||
public InputManager(Context context,
|
||||
WindowManagerService windowManagerService,
|
||||
WindowManagerPolicy windowManagerPolicy,
|
||||
@@ -215,37 +225,63 @@ public class InputManager {
|
||||
nativeUnregisterInputChannel(inputChannel);
|
||||
}
|
||||
|
||||
// TBD where this really belongs, duplicate copy in WindowManagerService
|
||||
static final int INJECT_FAILED = 0;
|
||||
static final int INJECT_SUCCEEDED = 1;
|
||||
static final int INJECT_NO_PERMISSION = -1;
|
||||
|
||||
/**
|
||||
* Injects a key event into the event system on behalf of an application.
|
||||
* This method may block even if sync is false because it must wait for previous events
|
||||
* to be dispatched before it can determine whether input event injection will be
|
||||
* permitted based on the current input focus.
|
||||
* @param event The event to inject.
|
||||
* @param nature The nature of the event.
|
||||
* @param injectorPid The pid of the injecting application.
|
||||
* @param injectorUid The uid of the injecting application.
|
||||
* @param sync If true, waits for the event to be completed before returning.
|
||||
* @param pid The pid of the injecting application.
|
||||
* @param uid The uid of the injecting application.
|
||||
* @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
|
||||
* @param timeoutMillis The injection timeout in milliseconds.
|
||||
* @return One of the INPUT_EVENT_INJECTION_XXX constants.
|
||||
*/
|
||||
public int injectKeyEvent(KeyEvent event, int nature, boolean sync, int pid, int uid) {
|
||||
// TODO
|
||||
return INJECT_FAILED;
|
||||
public int injectKeyEvent(KeyEvent event, int nature, int injectorPid, int injectorUid,
|
||||
boolean sync, int timeoutMillis) {
|
||||
if (event == null) {
|
||||
throw new IllegalArgumentException("event must not be null");
|
||||
}
|
||||
if (injectorPid < 0 || injectorUid < 0) {
|
||||
throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
|
||||
}
|
||||
if (timeoutMillis <= 0) {
|
||||
throw new IllegalArgumentException("timeoutMillis must be positive");
|
||||
}
|
||||
|
||||
return nativeInjectKeyEvent(event, nature, injectorPid, injectorUid,
|
||||
sync, timeoutMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a motion event into the event system on behalf of an application.
|
||||
* This method may block even if sync is false because it must wait for previous events
|
||||
* to be dispatched before it can determine whether input event injection will be
|
||||
* permitted based on the current input focus.
|
||||
* @param event The event to inject.
|
||||
* @param nature The nature of the event.
|
||||
* @param sync If true, waits for the event to be completed before returning.
|
||||
* @param pid The pid of the injecting application.
|
||||
* @param uid The uid of the injecting application.
|
||||
* @return INJECT_SUCCEEDED, INJECT_FAILED or INJECT_NO_PERMISSION
|
||||
* @param injectorPid The pid of the injecting application.
|
||||
* @param injectorUid The uid of the injecting application.
|
||||
* @param sync If true, waits for the event to be completed before returning.
|
||||
* @param timeoutMillis The injection timeout in milliseconds.
|
||||
* @return One of the INPUT_EVENT_INJECTION_XXX constants.
|
||||
*/
|
||||
public int injectMotionEvent(MotionEvent event, int nature, boolean sync, int pid, int uid) {
|
||||
// TODO
|
||||
return INJECT_FAILED;
|
||||
public int injectMotionEvent(MotionEvent event, int nature, int injectorPid, int injectorUid,
|
||||
boolean sync, int timeoutMillis) {
|
||||
if (event == null) {
|
||||
throw new IllegalArgumentException("event must not be null");
|
||||
}
|
||||
if (injectorPid < 0 || injectorUid < 0) {
|
||||
throw new IllegalArgumentException("injectorPid and injectorUid must not be negative.");
|
||||
}
|
||||
if (timeoutMillis <= 0) {
|
||||
throw new IllegalArgumentException("timeoutMillis must be positive");
|
||||
}
|
||||
|
||||
return nativeInjectMotionEvent(event, nature, injectorPid, injectorUid,
|
||||
sync, timeoutMillis);
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw) {
|
||||
@@ -271,8 +307,6 @@ public class InputManager {
|
||||
private static final boolean DEBUG_VIRTUAL_KEYS = false;
|
||||
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
|
||||
|
||||
private final InputTargetList mReusableInputTargetList = new InputTargetList();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public boolean isScreenOn() {
|
||||
return mPowerManagerService.isScreenOn();
|
||||
@@ -308,6 +342,21 @@ public class InputManager {
|
||||
mWindowManagerPolicy.notifyLidSwitchChanged(whenNanos, lidOpen);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void notifyInputChannelBroken(InputChannel inputChannel) {
|
||||
mWindowManagerService.notifyInputChannelBroken(inputChannel);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public long notifyInputChannelANR(InputChannel inputChannel) {
|
||||
return mWindowManagerService.notifyInputChannelANR(inputChannel);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void notifyInputChannelRecoveredFromANR(InputChannel inputChannel) {
|
||||
mWindowManagerService.notifyInputChannelRecoveredFromANR(inputChannel);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public int hackInterceptKey(int deviceId, int type, int scanCode,
|
||||
int keyCode, int policyFlags, int value, long whenNanos, boolean isScreenOn) {
|
||||
@@ -437,24 +486,23 @@ 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 InputTarget[] getKeyEventTargets(KeyEvent event, int nature, int policyFlags) {
|
||||
mReusableInputTargetList.clear();
|
||||
|
||||
mWindowManagerService.getKeyEventTargets(mReusableInputTargetList,
|
||||
event, nature, policyFlags);
|
||||
|
||||
return mReusableInputTargetList.toNullTerminatedArray();
|
||||
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 InputTarget[] getMotionEventTargets(MotionEvent event, int nature, int policyFlags) {
|
||||
mReusableInputTargetList.clear();
|
||||
|
||||
mWindowManagerService.getMotionEventTargets(mReusableInputTargetList,
|
||||
event, nature, policyFlags);
|
||||
|
||||
return mReusableInputTargetList.toNullTerminatedArray();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import android.view.InputTarget;
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class InputTargetList {
|
||||
public final class InputTargetList {
|
||||
private InputTarget[] mArray;
|
||||
private int mCount;
|
||||
|
||||
@@ -55,7 +55,7 @@ public class InputTargetList {
|
||||
count -= 1;
|
||||
mArray[count].recycle();
|
||||
}
|
||||
// mArray[0] could be set to null here but we do it in toNullTerminatedArray()
|
||||
mArray[0] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,7 +91,7 @@ public class InputTargetList {
|
||||
|
||||
mArray[mCount] = inputTarget;
|
||||
mCount += 1;
|
||||
// mArray[mCount] could be set to null here but we do it in toNullTerminatedArray()
|
||||
mArray[mCount] = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +99,6 @@ public class InputTargetList {
|
||||
* @return The input target array.
|
||||
*/
|
||||
public InputTarget[] toNullTerminatedArray() {
|
||||
mArray[mCount] = null;
|
||||
return mArray;
|
||||
}
|
||||
}
|
||||
@@ -203,6 +203,10 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
/** Adjustment to time to perform a dim, to make it more dramatic.
|
||||
*/
|
||||
static final int DIM_DURATION_MULTIPLIER = 6;
|
||||
|
||||
// Maximum number of milliseconds to wait for input event injection.
|
||||
// FIXME is this value reasonable?
|
||||
private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000;
|
||||
|
||||
static final int INJECT_FAILED = 0;
|
||||
static final int INJECT_SUCCEEDED = 1;
|
||||
@@ -447,8 +451,6 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>();
|
||||
final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>();
|
||||
|
||||
//flag to detect fat touch events
|
||||
boolean mFatTouch = false;
|
||||
Display mDisplay;
|
||||
|
||||
H mH = new H();
|
||||
@@ -5072,106 +5074,336 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
mPolicy.adjustConfigurationLw(config);
|
||||
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
|
||||
// -------------------------------------------------------------
|
||||
|
||||
public void getKeyEventTargets(InputTargetList inputTargets,
|
||||
KeyEvent event, int nature, int policyFlags) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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?
|
||||
|
||||
WindowState focus = getFocusedWindow();
|
||||
wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
|
||||
|
||||
addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
|
||||
}
|
||||
|
||||
// Target of Motion events
|
||||
WindowState mTouchFocus;
|
||||
|
||||
// Windows above the target who would like to receive an "outside"
|
||||
// touch event for any down events outside of them.
|
||||
// (This is a linked list by way of WindowState.mNextOutsideTouch.)
|
||||
WindowState mOutsideTouchTargets;
|
||||
|
||||
private void clearTouchFocus() {
|
||||
mTouchFocus = null;
|
||||
mOutsideTouchTargets = null;
|
||||
}
|
||||
|
||||
public void getMotionEventTargets(InputTargetList inputTargets,
|
||||
MotionEvent event, int nature, int policyFlags) {
|
||||
if (nature == InputQueue.INPUT_EVENT_NATURE_TRACKBALL) {
|
||||
// More or less the same as for keys...
|
||||
WindowState focus = getFocusedWindow();
|
||||
wakeupIfNeeded(focus, LocalPowerManager.BUTTON_EVENT);
|
||||
|
||||
addInputTarget(inputTargets, focus, InputTarget.FLAG_SYNC);
|
||||
return;
|
||||
if (! checkInjectionPermissionTd(focus, injectorPid, injectorUid)) {
|
||||
return InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED;
|
||||
}
|
||||
|
||||
int action = event.getAction();
|
||||
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;
|
||||
}
|
||||
|
||||
if (focus == null) {
|
||||
return InputManager.INPUT_EVENT_INJECTION_FAILED;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
default:
|
||||
return InputManager.INPUT_EVENT_INJECTION_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// TODO detect cheek presses somewhere... either here or in native code
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
mTouchDown = true;
|
||||
mPowerManager.logPointerDownEvent();
|
||||
|
||||
final boolean screenWasOff = (policyFlags & WindowManagerPolicy.FLAG_BRIGHT_HERE) != 0;
|
||||
|
||||
WindowState target = mTouchFocus;
|
||||
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
// let go of our target
|
||||
mPowerManager.logPointerUpEvent();
|
||||
clearTouchFocus();
|
||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||
// acquire a new target
|
||||
mPowerManager.logPointerDownEvent();
|
||||
|
||||
synchronized (mWindowMap) {
|
||||
if (mTouchFocus != null) {
|
||||
// this is weird, we got a pen down, but we thought it was
|
||||
// already down!
|
||||
// XXX: We should probably send an ACTION_UP to the current
|
||||
// target.
|
||||
Slog.w(TAG, "Pointer down received while already down in: "
|
||||
+ mTouchFocus);
|
||||
clearTouchFocus();
|
||||
synchronized (mWindowMap) {
|
||||
final int x = (int) event.getX();
|
||||
final int y = (int) event.getY();
|
||||
|
||||
final ArrayList windows = mWindows;
|
||||
final int N = windows.size();
|
||||
WindowState topErrWindow = null;
|
||||
final Rect tmpRect = mTempRect;
|
||||
for (int i= N - 1; i >= 0; i--) {
|
||||
WindowState child = (WindowState) windows.get(i);
|
||||
//Slog.i(TAG, "Checking dispatch to: " + child);
|
||||
|
||||
final int flags = child.mAttrs.flags;
|
||||
if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
|
||||
if (topErrWindow == null) {
|
||||
topErrWindow = child;
|
||||
}
|
||||
}
|
||||
|
||||
// ACTION_DOWN is special, because we need to lock next events to
|
||||
// the window we'll land onto.
|
||||
final int x = (int) event.getX();
|
||||
final int y = (int) event.getY();
|
||||
|
||||
final ArrayList windows = mWindows;
|
||||
final int N = windows.size();
|
||||
WindowState topErrWindow = null;
|
||||
final Rect tmpRect = mTempRect;
|
||||
for (int i=N-1; i>=0; i--) {
|
||||
WindowState child = (WindowState)windows.get(i);
|
||||
//Slog.i(TAG, "Checking dispatch to: " + child);
|
||||
final int flags = child.mAttrs.flags;
|
||||
if ((flags & WindowManager.LayoutParams.FLAG_SYSTEM_ERROR) != 0) {
|
||||
if (topErrWindow == null) {
|
||||
topErrWindow = child;
|
||||
}
|
||||
}
|
||||
if (!child.isVisibleLw()) {
|
||||
//Slog.i(TAG, "Not visible!");
|
||||
continue;
|
||||
}
|
||||
if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
|
||||
//Slog.i(TAG, "Not touchable!");
|
||||
if ((flags & WindowManager.LayoutParams
|
||||
.FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
|
||||
child.mNextOutsideTouch = mOutsideTouchTargets;
|
||||
mOutsideTouchTargets = child;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!child.isVisibleLw()) {
|
||||
//Slog.i(TAG, "Not visible!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
|
||||
tmpRect.set(child.mFrame);
|
||||
if (child.mTouchableInsets == ViewTreeObserver
|
||||
.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT) {
|
||||
@@ -5197,7 +5429,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
|
||||
if (tmpRect.contains(x, y) || touchFlags == 0) {
|
||||
//Slog.i(TAG, "Using this target!");
|
||||
if (!screenWasOff || (flags &
|
||||
if (! screenWasOff || (flags &
|
||||
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING) != 0) {
|
||||
mTouchFocus = child;
|
||||
} else {
|
||||
@@ -5206,143 +5438,76 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ((flags & WindowManager.LayoutParams
|
||||
.FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
|
||||
child.mNextOutsideTouch = mOutsideTouchTargets;
|
||||
mOutsideTouchTargets = child;
|
||||
//Slog.i(TAG, "Adding to outside target list: " + child);
|
||||
}
|
||||
}
|
||||
|
||||
// if there's an error window but it's not accepting
|
||||
// focus (typically because it is not yet visible) just
|
||||
// wait for it -- any other focused window may in fact
|
||||
// be in ANR state.
|
||||
if (topErrWindow != null && mTouchFocus != topErrWindow) {
|
||||
mTouchFocus = null;
|
||||
if ((flags & WindowManager.LayoutParams
|
||||
.FLAG_WATCH_OUTSIDE_TOUCH) != 0) {
|
||||
//Slog.i(TAG, "Adding to outside target list: " + child);
|
||||
mOutsideTouchTargets.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
target = mTouchFocus;
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
wakeupIfNeeded(target, eventType(event));
|
||||
}
|
||||
|
||||
int targetFlags = 0;
|
||||
if (target == null) {
|
||||
// In this case we are either dropping the event, or have received
|
||||
// a move or up without a down. It is common to receive move
|
||||
// events in such a way, since this means the user is moving the
|
||||
// pointer without actually pressing down. All other cases should
|
||||
// be atypical, so let's log them.
|
||||
if (action != MotionEvent.ACTION_MOVE) {
|
||||
Slog.w(TAG, "No window to dispatch pointer action " + action);
|
||||
}
|
||||
} else {
|
||||
if ((target.mAttrs.flags &
|
||||
WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
|
||||
//target wants to ignore fat touch events
|
||||
boolean cheekPress = mPolicy.isCheekPressedAgainstScreen(event);
|
||||
//explicit flag to return without processing event further
|
||||
boolean returnFlag = false;
|
||||
if((action == MotionEvent.ACTION_DOWN)) {
|
||||
mFatTouch = false;
|
||||
if(cheekPress) {
|
||||
mFatTouch = true;
|
||||
returnFlag = true;
|
||||
}
|
||||
} else {
|
||||
if(action == MotionEvent.ACTION_UP) {
|
||||
if(mFatTouch) {
|
||||
//earlier even was invalid doesnt matter if current up is cheekpress or not
|
||||
mFatTouch = false;
|
||||
returnFlag = true;
|
||||
} else if(cheekPress) {
|
||||
//cancel the earlier event
|
||||
targetFlags |= InputTarget.FLAG_CANCEL;
|
||||
action = MotionEvent.ACTION_CANCEL;
|
||||
}
|
||||
} else if(action == MotionEvent.ACTION_MOVE) {
|
||||
if(mFatTouch) {
|
||||
//two cases here
|
||||
//an invalid down followed by 0 or moves(valid or invalid)
|
||||
//a valid down, invalid move, more moves. want to ignore till up
|
||||
returnFlag = true;
|
||||
} else if(cheekPress) {
|
||||
//valid down followed by invalid moves
|
||||
//an invalid move have to cancel earlier action
|
||||
targetFlags |= InputTarget.FLAG_CANCEL;
|
||||
action = MotionEvent.ACTION_CANCEL;
|
||||
if (DEBUG_INPUT) Slog.v(TAG, "Sending cancel for invalid ACTION_MOVE");
|
||||
//note that the subsequent invalid moves will not get here
|
||||
mFatTouch = true;
|
||||
}
|
||||
}
|
||||
} //else if action
|
||||
if(returnFlag) {
|
||||
return;
|
||||
}
|
||||
} //end if target
|
||||
}
|
||||
|
||||
synchronized (mWindowMap) {
|
||||
if (target != null && ! target.isVisibleLw()) {
|
||||
target = null;
|
||||
|
||||
// If 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;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
while (mOutsideTouchTargets != null) {
|
||||
addInputTarget(inputTargets, mOutsideTouchTargets,
|
||||
InputTarget.FLAG_OUTSIDE | targetFlags);
|
||||
mOutsideTouchTargets = mOutsideTouchTargets.mNextOutsideTouch;
|
||||
}
|
||||
// Drop the touch focus if the window is not visible.
|
||||
if (mTouchFocus != null && ! mTouchFocus.isVisibleLw()) {
|
||||
mTouchFocus = null;
|
||||
}
|
||||
|
||||
// If we sent an initial down to the wallpaper, then continue
|
||||
// sending events until the final up.
|
||||
// Alternately if we are on top of the wallpaper, then the wallpaper also
|
||||
// gets to see this movement.
|
||||
if (mSendingPointersToWallpaper ||
|
||||
(target != null && action == MotionEvent.ACTION_DOWN
|
||||
&& mWallpaperTarget == target
|
||||
&& target.mAttrs.type != WindowManager.LayoutParams.TYPE_KEYGUARD)) {
|
||||
// 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) {
|
||||
continue;
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 0) {
|
||||
mWallpaperTouchTargets.add(wallpaper);
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mSendingPointersToWallpaper = true;
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
mSendingPointersToWallpaper = false;
|
||||
break;
|
||||
}
|
||||
|
||||
addInputTarget(inputTargets, wallpaper, targetFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
addInputTarget(inputTargets, target, InputTarget.FLAG_SYNC | targetFlags);
|
||||
}
|
||||
}
|
||||
|
||||
private void 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addInputTarget(InputTargetList inputTargets, WindowState window, int flags) {
|
||||
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;
|
||||
}
|
||||
@@ -5874,8 +6039,8 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|
||||
final int result;
|
||||
if (ENABLE_NATIVE_INPUT_DISPATCH) {
|
||||
result = mInputManager.injectKeyEvent(newEvent,
|
||||
InputQueue.INPUT_EVENT_NATURE_KEY, sync, pid, uid);
|
||||
result = mInputManager.injectKeyEvent(newEvent, InputQueue.INPUT_EVENT_NATURE_KEY,
|
||||
pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
|
||||
} else {
|
||||
result = dispatchKey(newEvent, pid, uid);
|
||||
if (sync) {
|
||||
@@ -5884,14 +6049,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
switch (result) {
|
||||
case INJECT_NO_PERMISSION:
|
||||
throw new SecurityException(
|
||||
"Injecting to another application requires INJECT_EVENTS permission");
|
||||
case INJECT_SUCCEEDED:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return reportInjectionResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5910,8 +6068,8 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|
||||
final int result;
|
||||
if (ENABLE_NATIVE_INPUT_DISPATCH) {
|
||||
result = mInputManager.injectMotionEvent(ev,
|
||||
InputQueue.INPUT_EVENT_NATURE_TOUCH, sync, pid, uid);
|
||||
result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TOUCH,
|
||||
pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
|
||||
} else {
|
||||
result = dispatchPointer(null, ev, pid, uid);
|
||||
if (sync) {
|
||||
@@ -5920,14 +6078,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
switch (result) {
|
||||
case INJECT_NO_PERMISSION:
|
||||
throw new SecurityException(
|
||||
"Injecting to another application requires INJECT_EVENTS permission");
|
||||
case INJECT_SUCCEEDED:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return reportInjectionResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5946,8 +6097,8 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|
||||
final int result;
|
||||
if (ENABLE_NATIVE_INPUT_DISPATCH) {
|
||||
result = mInputManager.injectMotionEvent(ev,
|
||||
InputQueue.INPUT_EVENT_NATURE_TRACKBALL, sync, pid, uid);
|
||||
result = mInputManager.injectMotionEvent(ev, InputQueue.INPUT_EVENT_NATURE_TRACKBALL,
|
||||
pid, uid, sync, INJECTION_TIMEOUT_MILLIS);
|
||||
} else {
|
||||
result = dispatchTrackball(null, ev, pid, uid);
|
||||
if (sync) {
|
||||
@@ -5956,14 +6107,37 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
switch (result) {
|
||||
case INJECT_NO_PERMISSION:
|
||||
throw new SecurityException(
|
||||
"Injecting to another application requires INJECT_EVENTS permission");
|
||||
case INJECT_SUCCEEDED:
|
||||
return true;
|
||||
return reportInjectionResult(result);
|
||||
}
|
||||
|
||||
private boolean reportInjectionResult(int result) {
|
||||
if (ENABLE_NATIVE_INPUT_DISPATCH) {
|
||||
switch (result) {
|
||||
case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED:
|
||||
Slog.w(TAG, "Input event injection permission denied.");
|
||||
throw new SecurityException(
|
||||
"Injecting to another application requires INJECT_EVENTS permission");
|
||||
case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED:
|
||||
Slog.v(TAG, "Input event injection succeeded.");
|
||||
return true;
|
||||
case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT:
|
||||
Slog.w(TAG, "Input event injection timed out.");
|
||||
return false;
|
||||
case InputManager.INPUT_EVENT_INJECTION_FAILED:
|
||||
default:
|
||||
Slog.w(TAG, "Input event injection failed.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
switch (result) {
|
||||
case INJECT_NO_PERMISSION:
|
||||
throw new SecurityException(
|
||||
"Injecting to another application requires INJECT_EVENTS permission");
|
||||
case INJECT_SUCCEEDED:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private WindowState getFocusedWindow() {
|
||||
|
||||
@@ -50,6 +50,9 @@ static struct {
|
||||
jmethodID isScreenBright;
|
||||
jmethodID notifyConfigurationChanged;
|
||||
jmethodID notifyLidSwitchChanged;
|
||||
jmethodID notifyInputChannelBroken;
|
||||
jmethodID notifyInputChannelANR;
|
||||
jmethodID notifyInputChannelRecoveredFromANR;
|
||||
jmethodID virtualKeyFeedback;
|
||||
jmethodID hackInterceptKey;
|
||||
jmethodID goToSleep;
|
||||
@@ -73,6 +76,13 @@ static struct {
|
||||
jfieldID height;
|
||||
} gVirtualKeyDefinitionClassInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
|
||||
jmethodID ctor;
|
||||
jfieldID mArray;
|
||||
} gInputTargetListClassInfo;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class NativeInputManager : public virtual RefBase,
|
||||
@@ -89,6 +99,10 @@ public:
|
||||
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);
|
||||
|
||||
/* --- InputReaderPolicyInterface implementation --- */
|
||||
|
||||
virtual bool getDisplayInfo(int32_t displayId,
|
||||
@@ -112,18 +126,20 @@ public:
|
||||
|
||||
virtual void notifyConfigurationChanged(nsecs_t when);
|
||||
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
|
||||
virtual void notifyInputChannelANR(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 void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
|
||||
Vector<InputTarget>& outTargets);
|
||||
virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
|
||||
Vector<InputTarget>& outTargets);
|
||||
virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
|
||||
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
|
||||
virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
|
||||
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
|
||||
|
||||
private:
|
||||
sp<InputManager> mInputManager;
|
||||
|
||||
jobject mCallbacksObj;
|
||||
jobject mReusableInputTargetListObj;
|
||||
|
||||
// Cached filtering policies.
|
||||
int32_t mFilterTouchEvents;
|
||||
@@ -138,12 +154,20 @@ private:
|
||||
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);
|
||||
|
||||
static inline JNIEnv* jniEnv() {
|
||||
return AndroidRuntime::getJNIEnv();
|
||||
}
|
||||
|
||||
static bool isAppSwitchKey(int32_t keyCode);
|
||||
static bool checkExceptionFromCallback(JNIEnv* env, const char* methodName);
|
||||
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
|
||||
static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
|
||||
Vector<InputTarget>& outTargets);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -155,6 +179,11 @@ NativeInputManager::NativeInputManager(jobject callbacksObj) :
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -163,13 +192,14 @@ NativeInputManager::~NativeInputManager() {
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
env->DeleteGlobalRef(mCallbacksObj);
|
||||
env->DeleteGlobalRef(mReusableInputTargetListObj);
|
||||
}
|
||||
|
||||
bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
|
||||
return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
|
||||
}
|
||||
|
||||
bool NativeInputManager::checkExceptionFromCallback(JNIEnv* env, const char* methodName) {
|
||||
bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
|
||||
if (env->ExceptionCheck()) {
|
||||
LOGE("An exception was thrown by callback '%s'.", methodName);
|
||||
LOGE_EX(env);
|
||||
@@ -196,6 +226,86 @@ void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orient
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -216,7 +326,7 @@ bool NativeInputManager::isScreenOn() {
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn);
|
||||
if (checkExceptionFromCallback(env, "isScreenOn")) {
|
||||
if (checkAndClearExceptionFromCallback(env, "isScreenOn")) {
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
@@ -226,7 +336,7 @@ bool NativeInputManager::isScreenBright() {
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright);
|
||||
if (checkExceptionFromCallback(env, "isScreenBright")) {
|
||||
if (checkAndClearExceptionFromCallback(env, "isScreenBright")) {
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
@@ -245,7 +355,7 @@ void NativeInputManager::virtualKeyFeedback(nsecs_t when, int32_t deviceId,
|
||||
|
||||
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback,
|
||||
when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
|
||||
checkExceptionFromCallback(env, "virtualKeyFeedback");
|
||||
checkAndClearExceptionFromCallback(env, "virtualKeyFeedback");
|
||||
}
|
||||
|
||||
int32_t NativeInputManager::interceptKey(nsecs_t when,
|
||||
@@ -267,7 +377,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
|
||||
|
||||
jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey,
|
||||
deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
|
||||
if (checkExceptionFromCallback(env, "hackInterceptKey")) {
|
||||
if (checkAndClearExceptionFromCallback(env, "hackInterceptKey")) {
|
||||
wmActions = 0;
|
||||
}
|
||||
|
||||
@@ -284,12 +394,12 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
|
||||
|
||||
if (wmActions & WM_ACTION_GO_TO_SLEEP) {
|
||||
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when);
|
||||
checkExceptionFromCallback(env, "goToSleep");
|
||||
checkAndClearExceptionFromCallback(env, "goToSleep");
|
||||
}
|
||||
|
||||
if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
|
||||
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when);
|
||||
checkExceptionFromCallback(env, "pokeUserActivityForKey");
|
||||
checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey");
|
||||
}
|
||||
|
||||
if (wmActions & WM_ACTION_PASS_TO_USER) {
|
||||
@@ -297,7 +407,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when,
|
||||
|
||||
if (down && isAppSwitchKey(keyCode)) {
|
||||
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
|
||||
checkExceptionFromCallback(env, "notifyAppSwitchComing");
|
||||
checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing");
|
||||
|
||||
actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
|
||||
}
|
||||
@@ -358,7 +468,7 @@ int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode,
|
||||
case SW_LID:
|
||||
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
|
||||
when, switchValue == 0);
|
||||
checkExceptionFromCallback(env, "notifyLidSwitchChanged");
|
||||
checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -371,7 +481,7 @@ bool NativeInputManager::filterTouchEvents() {
|
||||
|
||||
jboolean result = env->CallBooleanMethod(mCallbacksObj,
|
||||
gCallbacksClassInfo.filterTouchEvents);
|
||||
if (checkExceptionFromCallback(env, "filterTouchEvents")) {
|
||||
if (checkAndClearExceptionFromCallback(env, "filterTouchEvents")) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
@@ -386,7 +496,7 @@ bool NativeInputManager::filterJumpyTouchEvents() {
|
||||
|
||||
jboolean result = env->CallBooleanMethod(mCallbacksObj,
|
||||
gCallbacksClassInfo.filterJumpyTouchEvents);
|
||||
if (checkExceptionFromCallback(env, "filterJumpyTouchEvents")) {
|
||||
if (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
@@ -400,10 +510,10 @@ void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName,
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
jstring deviceNameStr = env->NewStringUTF(deviceName.string());
|
||||
if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
|
||||
if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
|
||||
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
|
||||
gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
|
||||
if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
|
||||
if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
|
||||
jsize length = env->GetArrayLength(result);
|
||||
for (jsize i = 0; i < length; i++) {
|
||||
jobject item = env->GetObjectArrayElement(result, i);
|
||||
@@ -433,7 +543,7 @@ void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDevi
|
||||
|
||||
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
|
||||
gCallbacksClassInfo.getExcludedDeviceNames));
|
||||
if (! checkExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
|
||||
if (! checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
|
||||
jsize length = env->GetArrayLength(result);
|
||||
for (jsize i = 0; i < length; i++) {
|
||||
jstring item = jstring(env->GetObjectArrayElement(result, i));
|
||||
@@ -460,7 +570,7 @@ void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
|
||||
|
||||
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
|
||||
when, config.touchScreen, config.keyboard, config.navigation);
|
||||
checkExceptionFromCallback(env, "notifyConfigurationChanged");
|
||||
checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
|
||||
}
|
||||
|
||||
void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
|
||||
@@ -468,16 +578,47 @@ void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputC
|
||||
LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
|
||||
#endif
|
||||
|
||||
// TODO
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
|
||||
if (inputChannelObjLocal) {
|
||||
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelBroken,
|
||||
inputChannelObjLocal);
|
||||
checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");
|
||||
|
||||
env->DeleteLocalRef(inputChannelObjLocal);
|
||||
}
|
||||
}
|
||||
|
||||
void NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) {
|
||||
bool NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel,
|
||||
nsecs_t& outNewTimeout) {
|
||||
#if DEBUG_INPUT_DISPATCHER_POLICY
|
||||
LOGD("notifyInputChannelANR - inputChannel='%s'",
|
||||
inputChannel->getName().string());
|
||||
#endif
|
||||
|
||||
// TODO
|
||||
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) {
|
||||
@@ -486,7 +627,16 @@ void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChanne
|
||||
inputChannel->getName().string());
|
||||
#endif
|
||||
|
||||
// TODO
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel);
|
||||
if (inputChannelObjLocal) {
|
||||
env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR,
|
||||
inputChannelObjLocal);
|
||||
checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR");
|
||||
|
||||
env->DeleteLocalRef(inputChannelObjLocal);
|
||||
}
|
||||
}
|
||||
|
||||
nsecs_t NativeInputManager::getKeyRepeatTimeout() {
|
||||
@@ -499,73 +649,83 @@ nsecs_t NativeInputManager::getKeyRepeatTimeout() {
|
||||
}
|
||||
}
|
||||
|
||||
void NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
|
||||
Vector<InputTarget>& outTargets) {
|
||||
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", policyFlags);
|
||||
LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
|
||||
policyFlags, injectorPid, injectorUid);
|
||||
#endif
|
||||
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
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;
|
||||
} else {
|
||||
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
|
||||
gCallbacksClassInfo.getKeyEventTargets,
|
||||
keyEventObj, jint(keyEvent->getNature()), jint(policyFlags)));
|
||||
if (! checkExceptionFromCallback(env, "getKeyEventTargets") && result) {
|
||||
jsize length = env->GetArrayLength(result);
|
||||
for (jsize i = 0; i < length; i++) {
|
||||
jobject item = env->GetObjectArrayElement(result, i);
|
||||
if (! item) {
|
||||
break; // found null element indicating end of used portion of the array
|
||||
}
|
||||
|
||||
outTargets.add();
|
||||
android_view_InputTarget_toNative(env, item, & outTargets.editTop());
|
||||
|
||||
env->DeleteLocalRef(item);
|
||||
}
|
||||
env->DeleteLocalRef(result);
|
||||
jint injectionResult = env->CallIntMethod(mCallbacksObj,
|
||||
gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj,
|
||||
keyEventObj, jint(keyEvent->getNature()), jint(policyFlags),
|
||||
jint(injectorPid), jint(injectorUid));
|
||||
if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) {
|
||||
injectionResult = INPUT_EVENT_INJECTION_FAILED;
|
||||
} else {
|
||||
populateInputTargets(env, mReusableInputTargetListObj, outTargets);
|
||||
}
|
||||
env->DeleteLocalRef(keyEventObj);
|
||||
}
|
||||
return injectionResult;
|
||||
}
|
||||
|
||||
void NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
|
||||
Vector<InputTarget>& outTargets) {
|
||||
int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
|
||||
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) {
|
||||
#if DEBUG_INPUT_DISPATCHER_POLICY
|
||||
LOGD("getMotionEventTargets - policyFlags=%d", policyFlags);
|
||||
LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d",
|
||||
policyFlags, injectorPid, injectorUid);
|
||||
#endif
|
||||
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
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 {
|
||||
jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
|
||||
gCallbacksClassInfo.getMotionEventTargets,
|
||||
motionEventObj, jint(motionEvent->getNature()), jint(policyFlags)));
|
||||
if (! checkExceptionFromCallback(env, "getMotionEventTargets") && result) {
|
||||
jsize length = env->GetArrayLength(result);
|
||||
for (jsize i = 0; i < length; i++) {
|
||||
jobject item = env->GetObjectArrayElement(result, i);
|
||||
if (! item) {
|
||||
break; // found null element indicating end of used portion of the array
|
||||
}
|
||||
|
||||
outTargets.add();
|
||||
android_view_InputTarget_toNative(env, item, & outTargets.editTop());
|
||||
|
||||
env->DeleteLocalRef(item);
|
||||
}
|
||||
env->DeleteLocalRef(result);
|
||||
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;
|
||||
} else {
|
||||
populateInputTargets(env, mReusableInputTargetListObj, outTargets);
|
||||
}
|
||||
android_view_MotionEvent_recycle(env, motionEventObj);
|
||||
env->DeleteLocalRef(motionEventObj);
|
||||
}
|
||||
return injectionResult;
|
||||
}
|
||||
|
||||
void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj,
|
||||
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
|
||||
}
|
||||
|
||||
outTargets.add();
|
||||
android_view_InputTarget_toNative(env, item, & outTargets.editTop());
|
||||
|
||||
env->DeleteLocalRef(item);
|
||||
}
|
||||
env->DeleteLocalRef(inputTargetArray);
|
||||
}
|
||||
|
||||
|
||||
@@ -686,7 +846,7 @@ static void android_server_InputManager_handleInputChannelDisposed(JNIEnv* env,
|
||||
"the input manager!", inputChannel->getName().string());
|
||||
|
||||
if (gNativeInputManager != NULL) {
|
||||
gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel);
|
||||
gNativeInputManager->unregisterInputChannel(env, inputChannel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,7 +863,9 @@ static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env,
|
||||
return;
|
||||
}
|
||||
|
||||
status_t status = gNativeInputManager->getInputManager()->registerInputChannel(inputChannel);
|
||||
|
||||
status_t status = gNativeInputManager->registerInputChannel(
|
||||
env, inputChannel, inputChannelObj);
|
||||
if (status) {
|
||||
jniThrowRuntimeException(env, "Failed to register input channel. "
|
||||
"Check logs for details.");
|
||||
@@ -729,13 +891,41 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env
|
||||
|
||||
android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
|
||||
|
||||
status_t status = gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel);
|
||||
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 JNINativeMethod gInputManagerMethods[] = {
|
||||
@@ -759,7 +949,11 @@ static JNINativeMethod gInputManagerMethods[] = {
|
||||
{ "nativeRegisterInputChannel", "(Landroid/view/InputChannel;)V",
|
||||
(void*) android_server_InputManager_nativeRegisterInputChannel },
|
||||
{ "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V",
|
||||
(void*) android_server_InputManager_nativeUnregisterInputChannel }
|
||||
(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 }
|
||||
};
|
||||
|
||||
#define FIND_CLASS(var, className) \
|
||||
@@ -796,6 +990,15 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
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.virtualKeyFeedback, gCallbacksClassInfo.clazz,
|
||||
"virtualKeyFeedback", "(JIIIIIIJ)V");
|
||||
|
||||
@@ -825,10 +1028,12 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
"getExcludedDeviceNames", "()[Ljava/lang/String;");
|
||||
|
||||
GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz,
|
||||
"getKeyEventTargets", "(Landroid/view/KeyEvent;II)[Landroid/view/InputTarget;");
|
||||
"getKeyEventTargets",
|
||||
"(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I");
|
||||
|
||||
GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz,
|
||||
"getMotionEventTargets", "(Landroid/view/MotionEvent;II)[Landroid/view/InputTarget;");
|
||||
"getMotionEventTargets",
|
||||
"(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I");
|
||||
|
||||
// VirtualKeyDefinition
|
||||
|
||||
@@ -850,6 +1055,16 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz,
|
||||
"height", "I");
|
||||
|
||||
// InputTargetList
|
||||
|
||||
FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList");
|
||||
|
||||
GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz,
|
||||
"<init>", "()V");
|
||||
|
||||
GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz,
|
||||
"mArray", "[Landroid/view/InputTarget;");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user