diff options
-rw-r--r-- | api/current.xml | 43 | ||||
-rw-r--r-- | core/java/android/view/IWindowManager.aidl | 6 | ||||
-rw-r--r-- | core/java/android/view/InputEvent.aidl | 20 | ||||
-rwxr-xr-x | core/java/android/view/InputEvent.java | 40 | ||||
-rwxr-xr-x | core/java/android/view/KeyEvent.java | 42 | ||||
-rw-r--r-- | core/java/android/view/MotionEvent.java | 88 | ||||
-rw-r--r-- | include/ui/InputDispatcher.h | 45 | ||||
-rw-r--r-- | include/ui/InputManager.h | 9 | ||||
-rw-r--r-- | libs/ui/InputDispatcher.cpp | 113 | ||||
-rw-r--r-- | libs/ui/InputManager.cpp | 4 | ||||
-rw-r--r-- | services/java/com/android/server/InputManager.java | 72 | ||||
-rw-r--r-- | services/java/com/android/server/WindowManagerService.java | 46 | ||||
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 55 |
13 files changed, 379 insertions, 204 deletions
diff --git a/api/current.xml b/api/current.xml index 1b7254f..f1a383f 100644 --- a/api/current.xml +++ b/api/current.xml @@ -174217,6 +174217,17 @@ > <implements name="android.os.Parcelable"> </implements> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getDevice" return="android.view.InputDevice" abstract="false" @@ -174250,6 +174261,16 @@ visibility="public" > </method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="mDeviceId" type="int" transient="false" @@ -174893,17 +174914,6 @@ <parameter name="newFlags" type="int"> </parameter> </method> -<method name="describeContents" - return="int" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> <method name="dispatch" return="boolean" abstract="false" @@ -178103,17 +178113,6 @@ <parameter name="metaState" type="int"> </parameter> </method> -<method name="describeContents" - return="int" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</method> <method name="findPointerIndex" return="int" abstract="false" diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 9b7b2f4..d6b9212 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -26,6 +26,7 @@ import android.view.IOnKeyguardExitResult; import android.view.IRotationWatcher; import android.view.IWindowSession; import android.view.KeyEvent; +import android.view.InputEvent; import android.view.MotionEvent; /** @@ -50,10 +51,13 @@ interface IWindowManager boolean inputMethodClientHasFocus(IInputMethodClient client); // These can only be called when injecting events to your own window, - // or by holding the INJECT_EVENTS permission. + // or by holding the INJECT_EVENTS permission. These methods may block + // until pending input events are finished being dispatched even when 'sync' is false. + // Avoid calling these methods on your UI thread or use the 'NoWait' version instead. boolean injectKeyEvent(in KeyEvent ev, boolean sync); boolean injectPointerEvent(in MotionEvent ev, boolean sync); boolean injectTrackballEvent(in MotionEvent ev, boolean sync); + boolean injectInputEventNoWait(in InputEvent ev); // These can only be called when holding the MANAGE_APP_TOKENS permission. void pauseKeyDispatching(IBinder token); diff --git a/core/java/android/view/InputEvent.aidl b/core/java/android/view/InputEvent.aidl new file mode 100644 index 0000000..61acaaf --- /dev/null +++ b/core/java/android/view/InputEvent.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android.view.InputEvent.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.view; + +parcelable InputEvent; diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java index 78b73fe..9afd16e 100755 --- a/core/java/android/view/InputEvent.java +++ b/core/java/android/view/InputEvent.java @@ -16,6 +16,7 @@ package android.view; +import android.os.Parcel; import android.os.Parcelable; /** @@ -25,6 +26,11 @@ public abstract class InputEvent implements Parcelable { protected int mDeviceId; protected int mSource; + /** @hide */ + protected static final int PARCEL_TOKEN_MOTION_EVENT = 1; + /** @hide */ + protected static final int PARCEL_TOKEN_KEY_EVENT = 2; + /*package*/ InputEvent() { } @@ -69,4 +75,38 @@ public abstract class InputEvent implements Parcelable { public final void setSource(int source) { mSource = source; } + + public final int describeContents() { + return 0; + } + + /** @hide */ + protected final void readBaseFromParcel(Parcel in) { + mDeviceId = in.readInt(); + mSource = in.readInt(); + } + + /** @hide */ + protected final void writeBaseToParcel(Parcel out) { + out.writeInt(mDeviceId); + out.writeInt(mSource); + } + + public static final Parcelable.Creator<InputEvent> CREATOR + = new Parcelable.Creator<InputEvent>() { + public InputEvent createFromParcel(Parcel in) { + int token = in.readInt(); + if (token == PARCEL_TOKEN_KEY_EVENT) { + return KeyEvent.createFromParcelBody(in); + } else if (token == PARCEL_TOKEN_MOTION_EVENT) { + return MotionEvent.createFromParcelBody(in); + } else { + throw new IllegalStateException("Unexpected input event type token in parcel."); + } + } + + public InputEvent[] newArray(int size) { + return new InputEvent[size]; + } + }; } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index dd0d21a..9223e17 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1211,44 +1211,48 @@ public class KeyEvent extends InputEvent implements Parcelable { public static final Parcelable.Creator<KeyEvent> CREATOR = new Parcelable.Creator<KeyEvent>() { public KeyEvent createFromParcel(Parcel in) { - return new KeyEvent(in); + in.readInt(); // skip token, we already know this is a KeyEvent + return KeyEvent.createFromParcelBody(in); } public KeyEvent[] newArray(int size) { return new KeyEvent[size]; } }; - - public int describeContents() { - return 0; + + /** @hide */ + public static KeyEvent createFromParcelBody(Parcel in) { + return new KeyEvent(in); + } + + private KeyEvent(Parcel in) { + readBaseFromParcel(in); + + mAction = in.readInt(); + mKeyCode = in.readInt(); + mRepeatCount = in.readInt(); + mMetaState = in.readInt(); + mScanCode = in.readInt(); + mFlags = in.readInt(); + mDownTime = in.readLong(); + mEventTime = in.readLong(); } public void writeToParcel(Parcel out, int flags) { + out.writeInt(PARCEL_TOKEN_KEY_EVENT); + + writeBaseToParcel(out); + out.writeInt(mAction); out.writeInt(mKeyCode); out.writeInt(mRepeatCount); out.writeInt(mMetaState); - out.writeInt(mDeviceId); - out.writeInt(mSource); out.writeInt(mScanCode); out.writeInt(mFlags); out.writeLong(mDownTime); out.writeLong(mEventTime); } - private KeyEvent(Parcel in) { - mAction = in.readInt(); - mKeyCode = in.readInt(); - mRepeatCount = in.readInt(); - mMetaState = in.readInt(); - mDeviceId = in.readInt(); - mSource = in.readInt(); - mScanCode = in.readInt(); - mFlags = in.readInt(); - mDownTime = in.readLong(); - mEventTime = in.readLong(); - } - private native boolean native_isSystemKey(int keyCode); private native boolean native_hasDefaultAction(int keyCode); } diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 0e78570..98fe73f 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1453,43 +1453,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { public static final Parcelable.Creator<MotionEvent> CREATOR = new Parcelable.Creator<MotionEvent>() { public MotionEvent createFromParcel(Parcel in) { - final int NP = in.readInt(); - final int NS = in.readInt(); - final int NI = NP * NS * NUM_SAMPLE_DATA; - - MotionEvent ev = obtain(NP, NS); - ev.mNumPointers = NP; - ev.mNumSamples = NS; - - ev.mDownTimeNano = in.readLong(); - ev.mAction = in.readInt(); - ev.mXOffset = in.readFloat(); - ev.mYOffset = in.readFloat(); - ev.mXPrecision = in.readFloat(); - ev.mYPrecision = in.readFloat(); - ev.mDeviceId = in.readInt(); - ev.mSource = in.readInt(); - ev.mEdgeFlags = in.readInt(); - ev.mMetaState = in.readInt(); - - final int[] pointerIdentifiers = ev.mPointerIdentifiers; - for (int i = 0; i < NP; i++) { - pointerIdentifiers[i] = in.readInt(); - } - - final long[] eventTimeNanoSamples = ev.mEventTimeNanoSamples; - for (int i = 0; i < NS; i++) { - eventTimeNanoSamples[i] = in.readLong(); - } - - final float[] dataSamples = ev.mDataSamples; - for (int i = 0; i < NI; i++) { - dataSamples[i] = in.readFloat(); - } - - ev.mLastEventTimeNanoSampleIndex = NS - 1; - ev.mLastDataSampleIndex = (NS - 1) * NP * NUM_SAMPLE_DATA; - return ev; + in.readInt(); // skip token, we already know this is a MotionEvent + return MotionEvent.createFromParcelBody(in); } public MotionEvent[] newArray(int size) { @@ -1497,11 +1462,50 @@ public final class MotionEvent extends InputEvent implements Parcelable { } }; - public int describeContents() { - return 0; - } + /** @hide */ + public static MotionEvent createFromParcelBody(Parcel in) { + final int NP = in.readInt(); + final int NS = in.readInt(); + final int NI = NP * NS * NUM_SAMPLE_DATA; + + MotionEvent ev = obtain(NP, NS); + ev.mNumPointers = NP; + ev.mNumSamples = NS; + + ev.readBaseFromParcel(in); + + ev.mDownTimeNano = in.readLong(); + ev.mAction = in.readInt(); + ev.mXOffset = in.readFloat(); + ev.mYOffset = in.readFloat(); + ev.mXPrecision = in.readFloat(); + ev.mYPrecision = in.readFloat(); + ev.mEdgeFlags = in.readInt(); + ev.mMetaState = in.readInt(); + + final int[] pointerIdentifiers = ev.mPointerIdentifiers; + for (int i = 0; i < NP; i++) { + pointerIdentifiers[i] = in.readInt(); + } + + final long[] eventTimeNanoSamples = ev.mEventTimeNanoSamples; + for (int i = 0; i < NS; i++) { + eventTimeNanoSamples[i] = in.readLong(); + } + final float[] dataSamples = ev.mDataSamples; + for (int i = 0; i < NI; i++) { + dataSamples[i] = in.readFloat(); + } + + ev.mLastEventTimeNanoSampleIndex = NS - 1; + ev.mLastDataSampleIndex = (NS - 1) * NP * NUM_SAMPLE_DATA; + return ev; + } + public void writeToParcel(Parcel out, int flags) { + out.writeInt(PARCEL_TOKEN_MOTION_EVENT); + final int NP = mNumPointers; final int NS = mNumSamples; final int NI = NP * NS * NUM_SAMPLE_DATA; @@ -1509,14 +1513,14 @@ public final class MotionEvent extends InputEvent implements Parcelable { out.writeInt(NP); out.writeInt(NS); + writeBaseToParcel(out); + out.writeLong(mDownTimeNano); out.writeInt(mAction); out.writeFloat(mXOffset); out.writeFloat(mYOffset); out.writeFloat(mXPrecision); out.writeFloat(mYPrecision); - out.writeInt(mDeviceId); - out.writeInt(mSource); out.writeInt(mEdgeFlags); out.writeInt(mMetaState); diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index 674852a..d3495fe 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -55,6 +55,22 @@ enum { INPUT_EVENT_INJECTION_TIMED_OUT = 3 }; +/* + * Constants used to determine the input event injection synchronization mode. + */ +enum { + /* Injection is asynchronous and is assumed always to be successful. */ + INPUT_EVENT_INJECTION_SYNC_NONE = 0, + + /* Waits for previous events to be dispatched so that the input dispatcher can determine + * whether input event injection willbe permitted based on the current input focus. + * Does not wait for the input event to finish processing. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, + + /* Waits for the input event to be completely processed. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, +}; + /* * An input target specifies how an input event is to be dispatched to a particular window @@ -176,15 +192,14 @@ public: 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. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. * 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; + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0; /* Preempts input dispatch in progress by making pending synchronous * dispatches asynchronous instead. This method is generally called during a focus @@ -241,7 +256,7 @@ public: 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); + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis); virtual void preemptInputDispatch(); @@ -267,11 +282,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 + int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING + bool injectionIsAsync; // set to true if injection is not waiting for the result + int32_t injectorPid; // -1 if not injected + int32_t injectorUid; // -1 if not injected bool dispatchInProgress; // initially false, set to true while dispatching + int32_t pendingSyncDispatches; // the number of synchronous dispatches in progress inline bool isInjected() { return injectorPid >= 0; } }; @@ -340,6 +357,10 @@ private: // headMotionSample will be initialized to tailMotionSample and tailMotionSample // will be set to NULL. MotionSample* tailMotionSample; + + inline bool isSyncTarget() { + return targetFlags & InputTarget::FLAG_SYNC; + } }; // A command entry captures state and behavior for an action to be performed in the @@ -497,8 +518,7 @@ private: // Since there can only ever be at most one such target at a time, if there is one, // it must be at the tail because nothing else can be enqueued after it. inline bool hasPendingSyncTarget() { - return ! outboundQueue.isEmpty() - && (outboundQueue.tail.prev->targetFlags & InputTarget::FLAG_SYNC); + return ! outboundQueue.isEmpty() && outboundQueue.tail.prev->isSyncTarget(); } // Gets the time since the current event was originally obtained from the input driver. @@ -559,11 +579,12 @@ private: // Event injection and synchronization. Condition mInjectionResultAvailableCondition; - Condition mFullySynchronizedCondition; - bool isFullySynchronizedLocked(); EventEntry* createEntryFromInputEventLocked(const InputEvent* event); void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult); + Condition mInjectionSyncFinishedCondition; + void decrementPendingSyncDispatchesLocked(EventEntry* entry); + // Key repeat tracking. // XXX Move this up to the input reader instead. struct KeyRepeatState { diff --git a/include/ui/InputManager.h b/include/ui/InputManager.h index 7ebec10..4012c69 100644 --- a/include/ui/InputManager.h +++ b/include/ui/InputManager.h @@ -79,13 +79,12 @@ public: 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. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. * 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; + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) = 0; /* Preempts input dispatch in progress by making pending synchronous * dispatches asynchronous instead. This method is generally called during a focus @@ -142,7 +141,7 @@ public: 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); + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis); virtual void preemptInputDispatch(); diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index a55864b..b53f140 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -184,11 +184,6 @@ 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. @@ -560,6 +555,10 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, dispatchEntry->headMotionSample = NULL; dispatchEntry->tailMotionSample = NULL; + if (dispatchEntry->isSyncTarget()) { + eventEntry->pendingSyncDispatches += 1; + } + // Handle the case where we could not stream a new motion sample because the consumer has // already consumed the motion event (otherwise the corresponding dispatch entry would // still be in the outbound queue for this connection). We set the head motion sample @@ -789,6 +788,9 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, } // Finished. connection->outboundQueue.dequeueAtHead(); + if (dispatchEntry->isSyncTarget()) { + decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); + } mAllocator.releaseDispatchEntry(dispatchEntry); } else { // If the head is not in progress, then we must have already dequeued the in @@ -854,6 +856,9 @@ void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, if (! connection->outboundQueue.isEmpty()) { do { DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); + if (dispatchEntry->isSyncTarget()) { + decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); + } mAllocator.releaseDispatchEntry(dispatchEntry); } while (! connection->outboundQueue.isEmpty()); @@ -1097,7 +1102,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t Connection* connection = mActiveConnections.itemAt(i); if (! connection->outboundQueue.isEmpty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev; - if (dispatchEntry->targetFlags & InputTarget::FLAG_SYNC) { + if (dispatchEntry->isSyncTarget()) { if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { goto NoBatchingOrStreaming; } @@ -1148,11 +1153,11 @@ NoBatchingOrStreaming:; } int32_t InputDispatcher::injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, bool sync, int32_t timeoutMillis) { + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, 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); + "syncMode=%d, timeoutMillis=%d", + event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis); #endif nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); @@ -1167,6 +1172,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, injectedEntry->injectorPid = injectorPid; injectedEntry->injectorUid = injectorUid; + if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { + injectedEntry->injectionIsAsync = true; + } + wasEmpty = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(injectedEntry); @@ -1180,37 +1189,59 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, { // 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 (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + } else { + for (;;) { + injectionResult = injectedEntry->injectionResult; + if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { + break; + } - if (sync) { - while (! isFullySynchronizedLocked()) { nsecs_t remainingTimeout = endTime - now(); if (remainingTimeout <= 0) { +#if DEBUG_INJECTION + LOGD("injectInputEvent - Timed out waiting for injection result " + "to become available."); +#endif injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; break; } - mFullySynchronizedCondition.waitRelative(mLock, remainingTimeout); + mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout); + } + + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED + && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { + while (injectedEntry->pendingSyncDispatches != 0) { +#if DEBUG_INJECTION + LOGD("injectInputEvent - Waiting for %d pending synchronous dispatches.", + injectedEntry->pendingSyncDispatches); +#endif + nsecs_t remainingTimeout = endTime - now(); + if (remainingTimeout <= 0) { +#if DEBUG_INJECTION + LOGD("injectInputEvent - Timed out waiting for pending synchronous " + "dispatches to finish."); +#endif + injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + break; + } + + mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout); + } } } mAllocator.releaseEventEntry(injectedEntry); } // release lock +#if DEBUG_INJECTION + LOGD("injectInputEvent - Finished with result %d. " + "injectorPid=%d, injectorUid=%d", + injectionResult, injectorPid, injectorUid); +#endif + return injectionResult; } @@ -1222,13 +1253,35 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject injectionResult, entry->injectorPid, entry->injectorUid); #endif + if (entry->injectionIsAsync) { + // Log the outcome since the injector did not wait for the injection result. + switch (injectionResult) { + case INPUT_EVENT_INJECTION_SUCCEEDED: + LOGV("Asynchronous input event injection succeeded."); + break; + case INPUT_EVENT_INJECTION_FAILED: + LOGW("Asynchronous input event injection failed."); + break; + case INPUT_EVENT_INJECTION_PERMISSION_DENIED: + LOGW("Asynchronous input event injection permission denied."); + break; + case INPUT_EVENT_INJECTION_TIMED_OUT: + LOGW("Asynchronous input event injection timed out."); + break; + } + } + entry->injectionResult = injectionResult; mInjectionResultAvailableCondition.broadcast(); } } -bool InputDispatcher::isFullySynchronizedLocked() { - return mInboundQueue.isEmpty() && mActiveConnections.isEmpty(); +void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) { + entry->pendingSyncDispatches -= 1; + + if (entry->isInjected() && entry->pendingSyncDispatches == 0) { + mInjectionSyncFinishedCondition.broadcast(); + } } InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( @@ -1498,8 +1551,10 @@ void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t entry->dispatchInProgress = false; entry->eventTime = eventTime; entry->injectionResult = INPUT_EVENT_INJECTION_PENDING; + entry->injectionIsAsync = false; entry->injectorPid = -1; entry->injectorUid = -1; + entry->pendingSyncDispatches = 0; } InputDispatcher::ConfigurationChangedEntry* diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index bf23479..ed4f07b 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -81,8 +81,8 @@ status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChann } 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); + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) { + return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis); } void InputManager::preemptInputDispatch() { diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index 9a1d017..9195123 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -29,6 +29,7 @@ import android.os.PowerManager; import android.util.Slog; import android.util.Xml; import android.view.InputChannel; +import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; @@ -76,10 +77,8 @@ public class InputManager { 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 injectorPid, int injectorUid, boolean sync, int timeoutMillis); - private static native int nativeInjectMotionEvent(MotionEvent event, - int injectorPid, int injectorUid, boolean sync, int timeoutMillis); + private static native int nativeInjectInputEvent(InputEvent event, + int injectorPid, int injectorUid, int syncMode, int timeoutMillis); private static native void nativeSetInputWindows(InputWindow[] windows); private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen); private static native void nativeSetFocusedApplication(InputApplication application); @@ -92,6 +91,11 @@ public class InputManager { static final int INPUT_EVENT_INJECTION_FAILED = 2; static final int INPUT_EVENT_INJECTION_TIMED_OUT = 3; + // Input event injection synchronization modes defined in InputDispatcher.h + static final int INPUT_EVENT_INJECTION_SYNC_NONE = 0; + static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1; + static final int INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH = 2; + // Key states (may be returned by queries about the current state of a // particular key code, scan code or switch). @@ -107,7 +111,6 @@ public class InputManager { /** The key is down but is a virtual key press that is being emulated by the system. */ public static final int KEY_STATE_VIRTUAL = 2; - public InputManager(Context context, WindowManagerService windowManagerService) { this.mContext = context; this.mWindowManagerService = windowManagerService; @@ -239,19 +242,30 @@ public class InputManager { } /** - * 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. + * Injects an input event into the event system on behalf of an application. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. + * + * {@link #INPUT_EVENT_INJECTION_SYNC_NONE} never blocks. Injection is asynchronous and + * is assumed always to be successful. + * + * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT} waits for previous events to be + * dispatched so that the input dispatcher can determine whether input event injection will + * be permitted based on the current input focus. Does not wait for the input event to + * finish processing. + * + * {@link #INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH} waits for the input event to + * be completely processed. + * * @param event The event to inject. * @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 syncMode The synchronization mode. * @param timeoutMillis The injection timeout in milliseconds. * @return One of the INPUT_EVENT_INJECTION_XXX constants. */ - public int injectKeyEvent(KeyEvent event, int injectorPid, int injectorUid, - boolean sync, int timeoutMillis) { + public int injectInputEvent(InputEvent event, int injectorPid, int injectorUid, + int syncMode, int timeoutMillis) { if (event == null) { throw new IllegalArgumentException("event must not be null"); } @@ -261,38 +275,8 @@ public class InputManager { if (timeoutMillis <= 0) { throw new IllegalArgumentException("timeoutMillis must be positive"); } - - return nativeInjectKeyEvent(event, 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 sync If true, waits for the event to be completed before returning. - * @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 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, injectorPid, injectorUid, - sync, timeoutMillis); + + return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis); } public void setInputWindows(InputWindow[] windows) { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index eb0f343..5615232 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -103,6 +103,7 @@ import android.view.IWindowManager; import android.view.IWindowSession; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputEvent; import android.view.InputQueue; import android.view.KeyEvent; import android.view.MotionEvent; @@ -5382,6 +5383,8 @@ public class WindowManagerService extends IWindowManager.Stub /** * Injects a keystroke event into the UI. + * Even when sync is false, this method may block while waiting for current + * input events to be dispatched. * * @param ev A motion event describing the keystroke action. (Be sure to use * {@link SystemClock#uptimeMillis()} as the timebase.) @@ -5414,8 +5417,10 @@ public class WindowManagerService extends IWindowManager.Stub final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); - final int result = mInputManager.injectKeyEvent(newEvent, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); + final int result = mInputManager.injectInputEvent(newEvent, pid, uid, + sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH + : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); @@ -5423,6 +5428,8 @@ public class WindowManagerService extends IWindowManager.Stub /** * Inject a pointer (touch) event into the UI. + * Even when sync is false, this method may block while waiting for current + * input events to be dispatched. * * @param ev A motion event describing the pointer (touch) action. (As noted in * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use @@ -5440,8 +5447,10 @@ public class WindowManagerService extends IWindowManager.Stub newEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); } - final int result = mInputManager.injectMotionEvent(newEvent, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); + final int result = mInputManager.injectInputEvent(newEvent, pid, uid, + sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH + : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); @@ -5449,6 +5458,8 @@ public class WindowManagerService extends IWindowManager.Stub /** * Inject a trackball (navigation device) event into the UI. + * Even when sync is false, this method may block while waiting for current + * input events to be dispatched. * * @param ev A motion event describing the trackball action. (As noted in * {@link MotionEvent#obtain(long, long, int, float, float, int)}, be sure to use @@ -5466,8 +5477,31 @@ public class WindowManagerService extends IWindowManager.Stub newEvent.setSource(InputDevice.SOURCE_TRACKBALL); } - final int result = mInputManager.injectMotionEvent(newEvent, - pid, uid, sync, INJECTION_TIMEOUT_MILLIS); + final int result = mInputManager.injectInputEvent(newEvent, pid, uid, + sync ? InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISH + : InputManager.INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECTION_TIMEOUT_MILLIS); + + Binder.restoreCallingIdentity(ident); + return reportInjectionResult(result); + } + + /** + * Inject an input event into the UI without waiting for dispatch to commence. + * This variant is useful for fire-and-forget input event injection. It does not + * block any longer than it takes to enqueue the input event. + * + * @param ev An input event. (Be sure to set the input source correctly.) + * @return Returns true if event was dispatched, false if it was dropped for any reason + */ + public boolean injectInputEventNoWait(InputEvent ev) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + + final int result = mInputManager.injectInputEvent(ev, pid, uid, + InputManager.INPUT_EVENT_INJECTION_SYNC_NONE, + INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); return reportInjectionResult(result); diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index a332376..0982b32 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -180,6 +180,14 @@ static struct { jfieldID token; } gInputApplicationClassInfo; +static struct { + jclass clazz; +} gKeyEventClassInfo; + +static struct { + jclass clazz; +} gMotionEventClassInfo; + // ---------------------------------------------------------------------------- static inline nsecs_t now() { @@ -2051,32 +2059,29 @@ static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env } } -static jint android_server_InputManager_nativeInjectKeyEvent(JNIEnv* env, jclass clazz, - jobject keyEventObj, jint injectorPid, jint injectorUid, - jboolean sync, jint timeoutMillis) { +static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jclass clazz, + jobject inputEventObj, jint injectorPid, jint injectorUid, + jint syncMode, jint timeoutMillis) { if (checkInputManagerUnitialized(env)) { return INPUT_EVENT_INJECTION_FAILED; } - KeyEvent keyEvent; - android_view_KeyEvent_toNative(env, keyEventObj, & keyEvent); + if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) { + KeyEvent keyEvent; + android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent); - return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent, - injectorPid, injectorUid, sync, timeoutMillis); -} + return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent, + injectorPid, injectorUid, syncMode, timeoutMillis); + } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) { + MotionEvent motionEvent; + android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent); -static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jclass clazz, - jobject motionEventObj, jint injectorPid, jint injectorUid, - jboolean sync, jint timeoutMillis) { - if (checkInputManagerUnitialized(env)) { + return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent, + injectorPid, injectorUid, syncMode, timeoutMillis); + } else { + jniThrowRuntimeException(env, "Invalid input event type."); return INPUT_EVENT_INJECTION_FAILED; } - - MotionEvent motionEvent; - android_view_MotionEvent_toNative(env, motionEventObj, & motionEvent); - - return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent, - injectorPid, injectorUid, sync, timeoutMillis); } static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz, @@ -2148,10 +2153,8 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeRegisterInputChannel }, { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V", (void*) android_server_InputManager_nativeUnregisterInputChannel }, - { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIZI)I", - (void*) android_server_InputManager_nativeInjectKeyEvent }, - { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIZI)I", - (void*) android_server_InputManager_nativeInjectMotionEvent }, + { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I", + (void*) android_server_InputManager_nativeInjectInputEvent }, { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V", (void*) android_server_InputManager_nativeSetInputWindows }, { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V", @@ -2318,6 +2321,14 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz, "token", "Ljava/lang/Object;"); + // KeyEvent + + FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); + + // MotionEVent + + FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); + return 0; } |