Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75a91389 authored by Dianne Hackborn's avatar Dianne Hackborn Committed by Android (Google) Code Review
Browse files

Merge "Implement native key pre-dispatching to IMEs." into gingerbread

parents 02c8730c 2c6081ce
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
package android.app;

import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;

import dalvik.system.PathClassLoader;

import android.content.Context;
@@ -25,6 +28,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.inputmethod.InputMethodManager;

import java.io.File;
import java.lang.ref.WeakReference;

/**
 * Convenience for implementing an activity that will be implemented
@@ -36,6 +40,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
    
    private NativeContentView mNativeContentView;
    private InputMethodManager mIMM;
    private InputMethodCallback mInputMethodCallback;

    private int mNativeHandle;
    
@@ -73,6 +78,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
    private native void onInputChannelDestroyedNative(int handle, InputChannel channel);
    private native void onContentRectChangedNative(int handle, int x, int y, int w, int h);
    private native void dispatchKeyEventNative(int handle, KeyEvent event);
    private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled);

    static class NativeContentView extends View {
        NativeActivity mActivity;
@@ -86,12 +92,34 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
        }
    }
    
    static class InputMethodCallback extends IInputMethodCallback.Stub {
        WeakReference<NativeActivity> mNa;

        InputMethodCallback(NativeActivity na) {
            mNa = new WeakReference<NativeActivity>(na);
        }

        @Override
        public void finishedEvent(int seq, boolean handled) {
            NativeActivity na = mNa.get();
            if (na != null) {
                na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled);
            }
        }

        @Override
        public void sessionCreated(IInputMethodSession session) {
            // Stub -- not for use in the client.
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        String libname = "main";
        ActivityInfo ai;
        
        mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        mInputMethodCallback = new InputMethodCallback(this);

        getWindow().takeSurface(this);
        getWindow().takeInputQueue(this);
@@ -292,6 +320,11 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
        }
    }
    
    void preDispatchKeyEvent(KeyEvent event, int seq) {
        mIMM.dispatchKeyEvent(this, seq, event,
                mInputMethodCallback);
    }

    void setWindowFlags(int flags, int mask) {
        getWindow().setFlags(flags, mask);
    }
+208 −44
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ static struct {
    jclass clazz;

    jmethodID dispatchUnhandledKeyEvent;
    jmethodID preDispatchKeyEvent;
    jmethodID setWindowFlags;
    jmethodID setWindowFormat;
    jmethodID showIme;
@@ -104,7 +105,7 @@ static bool read_work(int fd, ActivityWork* outWork) {
using namespace android;

AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
        mWorkWrite(workWrite), mConsumer(channel) {
        mWorkWrite(workWrite), mConsumer(channel), mSeq(0) {
    int msgpipe[2];
    if (pipe(msgpipe)) {
        LOGW("could not create pipe: %s", strerror(errno));
@@ -157,6 +158,8 @@ int32_t AInputQueue::hasEvents() {
int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
    *outEvent = NULL;

    bool finishNow = false;

    char byteread;
    ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
    if (nRead == 1) {
@@ -165,10 +168,34 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
            KeyEvent* kevent = mDispatchingKeys[0];
            *outEvent = kevent;
            mDispatchingKeys.removeAt(0);
            mDeliveringKeys.add(kevent);
            in_flight_event inflight;
            inflight.event = kevent;
            inflight.seq = -1;
            inflight.doFinish = false;
            mInFlightEvents.push(inflight);
        }
        if (mFinishPreDispatches.size() > 0) {
            finish_pre_dispatch finish(mFinishPreDispatches[0]);
            mFinishPreDispatches.removeAt(0);
            const size_t N = mInFlightEvents.size();
            for (size_t i=0; i<N; i++) {
                const in_flight_event& inflight(mInFlightEvents[i]);
                if (inflight.seq == finish.seq) {
                    *outEvent = inflight.event;
                    finishNow = finish.handled;
                }
            }
            if (*outEvent == NULL) {
                LOGW("getEvent couldn't find inflight for seq %d", finish.seq);
            }
        }
        mLock.unlock();
        if (*outEvent != NULL) {

        if (finishNow) {
            finishEvent(*outEvent, true);
            *outEvent = NULL;
            return -1;
        } else if (*outEvent != NULL) {
            return 0;
        }
    }
@@ -181,7 +208,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
    }

    InputEvent* myEvent = NULL;
    res = mConsumer.consume(&mInputEventFactory, &myEvent);
    res = mConsumer.consume(this, &myEvent);
    if (res != android::OK) {
        LOGW("channel '%s' ~ Failed to consume input event.  status=%d",
                mConsumer.getChannel()->getName().string(), res);
@@ -189,39 +216,69 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
        return -1;
    }

    in_flight_event inflight;
    inflight.event = myEvent;
    inflight.seq = -1;
    inflight.doFinish = true;
    mInFlightEvents.push(inflight);

    *outEvent = myEvent;
    return 0;
}

bool AInputQueue::preDispatchEvent(AInputEvent* event) {
    if (((InputEvent*)event)->getType() != AINPUT_EVENT_TYPE_KEY) {
        // The IME only cares about key events.
        return false;
    }

    // For now we only send system keys to the IME...  this avoids having
    // critical keys like DPAD go through this path.  We really need to have
    // the IME report which keys it wants.
    if (!((KeyEvent*)event)->isSystemKey()) {
        return false;
    }

    return preDispatchKey((KeyEvent*)event);
}

void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
    bool needFinished = true;
    LOG_TRACE("finishEvent: %p handled=%d", event, handled ? 1 : 0);

    if (!handled && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
            && ((KeyEvent*)event)->hasDefaultAction()) {
        // The app didn't handle this, but it may have a default action
        // associated with it.  We need to hand this back to Java to be
        // executed.
        doDefaultKey((KeyEvent*)event);
        needFinished = false;
        doUnhandledKey((KeyEvent*)event);
        return;
    }

    const size_t N = mDeliveringKeys.size();
    mLock.lock();
    const size_t N = mInFlightEvents.size();
    for (size_t i=0; i<N; i++) {
        if (mDeliveringKeys[i] == event) {
            delete event;
            mDeliveringKeys.removeAt(i);
            needFinished = false;
            break;
        }
    }
    
    if (needFinished) {
        const in_flight_event& inflight(mInFlightEvents[i]);
        if (inflight.event == event) {
            if (inflight.doFinish) {
                int32_t res = mConsumer.sendFinishedSignal();
                if (res != android::OK) {
                    LOGW("Failed to send finished signal on channel '%s'.  status=%d",
                            mConsumer.getChannel()->getName().string(), res);
                }
            }
            if (static_cast<InputEvent*>(event)->getType() == AINPUT_EVENT_TYPE_KEY) {
                mAvailKeyEvents.push(static_cast<KeyEvent*>(event));
            } else {
                mAvailMotionEvents.push(static_cast<MotionEvent*>(event));
            }
            mInFlightEvents.removeAt(i);
            mLock.unlock();
            return;
        }
    }
    mLock.unlock();
    
    LOGW("finishEvent called for unknown event: %p", event);
}

void AInputQueue::dispatchEvent(android::KeyEvent* event) {
@@ -229,28 +286,28 @@ void AInputQueue::dispatchEvent(android::KeyEvent* event) {
    LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
            mDispatchKeyWrite);
    mDispatchingKeys.add(event);
    wakeupDispatch();
    mLock.unlock();
    
restart:
    char dummy = 0;
    int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
    if (res < 0 && errno == EINTR) {
        goto restart;
}

    if (res == sizeof(dummy)) return;

    if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno));
    else LOGW("Truncated writing to dispatch fd: %d", res);
void AInputQueue::finishPreDispatch(int seq, bool handled) {
    mLock.lock();
    LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0);
    finish_pre_dispatch finish;
    finish.seq = seq;
    finish.handled = handled;
    mFinishPreDispatches.add(finish);
    wakeupDispatch();
    mLock.unlock();
}

KeyEvent* AInputQueue::consumeUnhandledEvent() {
    KeyEvent* event = NULL;

    mLock.lock();
    if (mPendingKeys.size() > 0) {
        event = mPendingKeys[0];
        mPendingKeys.removeAt(0);
    if (mUnhandledKeys.size() > 0) {
        event = mUnhandledKeys[0];
        mUnhandledKeys.removeAt(0);
    }
    mLock.unlock();

@@ -259,14 +316,101 @@ KeyEvent* AInputQueue::consumeUnhandledEvent() {
    return event;
}

void AInputQueue::doDefaultKey(KeyEvent* keyEvent) {
KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) {
    KeyEvent* event = NULL;

    mLock.lock();
    if (mPreDispatchingKeys.size() > 0) {
        const in_flight_event& inflight(mPreDispatchingKeys[0]);
        event = static_cast<KeyEvent*>(inflight.event);
        *outSeq = inflight.seq;
        mPreDispatchingKeys.removeAt(0);
    }
    mLock.unlock();

    LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event);

    return event;
}

KeyEvent* AInputQueue::createKeyEvent() {
    mLock.lock();
    KeyEvent* event;
    if (mAvailKeyEvents.size() <= 0) {
        event = new KeyEvent();
    } else {
        event = mAvailKeyEvents.top();
        mAvailKeyEvents.pop();
    }
    mLock.unlock();
    return event;
}

MotionEvent* AInputQueue::createMotionEvent() {
    mLock.lock();
    MotionEvent* event;
    if (mAvailMotionEvents.size() <= 0) {
        event = new MotionEvent();
    } else {
        event = mAvailMotionEvents.top();
        mAvailMotionEvents.pop();
    }
    mLock.unlock();
    return event;
}

void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) {
    mLock.lock();
    LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite);
    if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) {
        write_work(mWorkWrite, CMD_DEF_KEY);
    }
    mUnhandledKeys.add(keyEvent);
    mLock.unlock();
}

bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
    mLock.lock();
    LOG_TRACE("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
    if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
    LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite);
    const size_t N = mInFlightEvents.size();
    for (size_t i=0; i<N; i++) {
        in_flight_event& inflight(mInFlightEvents.editItemAt(i));
        if (inflight.event == keyEvent) {
            if (inflight.seq >= 0) {
                // This event has already been pre-dispatched!
                LOG_TRACE("Event already pre-dispatched!");
                mLock.unlock();
                return false;
            }
            mSeq++;
            if (mSeq < 0) mSeq = 1;
            inflight.seq = mSeq;

            if (mPreDispatchingKeys.size() <= 0 && mWorkWrite >= 0) {
                write_work(mWorkWrite, CMD_DEF_KEY);
            }
    mPendingKeys.add(keyEvent);
            mPreDispatchingKeys.add(inflight);
            mLock.unlock();
            return true;
        }
    }

    LOGW("preDispatchKey called for unknown event: %p", keyEvent);
    return false;
}

void AInputQueue::wakeupDispatch() {
restart:
    char dummy = 0;
    int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
    if (res < 0 && errno == EINTR) {
        goto restart;
    }

    if (res == sizeof(dummy)) return;

    if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno));
    else LOGW("Truncated writing to dispatch fd: %d", res);
}

namespace android {
@@ -417,11 +561,14 @@ static bool mainWorkCallback(int fd, int events, void* data) {
                        code->env, keyEvent);
                code->env->CallVoidMethod(code->clazz,
                        gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
                int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
                if (res != OK) {
                    LOGW("Failed to send finished signal on channel '%s'.  status=%d",
                            code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
                code->nativeInputQueue->finishEvent(keyEvent, true);
            }
            int seq;
            while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
                jobject inputEventObj = android_view_KeyEvent_fromNative(
                        code->env, keyEvent);
                code->env->CallVoidMethod(code->clazz,
                        gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
            }
        } break;
        case CMD_SET_WINDOW_FORMAT: {
@@ -766,13 +913,26 @@ dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventOb
    if (handle != 0) {
        NativeCode* code = (NativeCode*)handle;
        if (code->nativeInputQueue != NULL) {
            KeyEvent* event = new KeyEvent();
            KeyEvent* event = code->nativeInputQueue->createKeyEvent();
            android_view_KeyEvent_toNative(env, eventObj, event);
            code->nativeInputQueue->dispatchEvent(event);
        }
    }
}

static void
finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
        jint seq, jboolean handled)
{
    LOG_TRACE("finishPreDispatchKeyEvent_native");
    if (handle != 0) {
        NativeCode* code = (NativeCode*)handle;
        if (code->nativeInputQueue != NULL) {
            code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false);
        }
    }
}

static const JNINativeMethod g_methods[] = {
    { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
            (void*)loadNativeCode_native },
@@ -792,6 +952,7 @@ static const JNINativeMethod g_methods[] = {
    { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
    { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
    { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
    { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
};

static const char* const kNativeActivityPathName = "android/app/NativeActivity";
@@ -814,6 +975,9 @@ int register_android_app_NativeActivity(JNIEnv* env)
    GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
            gNativeActivityClassInfo.clazz,
            "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
    GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
            gNativeActivityClassInfo.clazz,
            "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");

    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
            gNativeActivityClassInfo.clazz,
+63 −9
Original line number Diff line number Diff line
@@ -42,8 +42,26 @@ extern void android_NativeActivity_hideSoftInput(

/*
 * NDK input queue API.
 *
 * Here is the event flow:
 * 1. Event arrives in input consumer, and is returned by getEvent().
 * 2. Application calls preDispatchEvent():
 *    a. Event is assigned a sequence ID and enqueued in mPreDispatchingKeys.
 *    b. Main thread picks up event, hands to input method.
 *    c. Input method eventually returns sequence # and whether it was handled.
 *    d. finishPreDispatch() is called to enqueue the information.
 *    e. next getEvent() call will:
 *       - finish any pre-dispatch events that the input method handled
 *       - return the next pre-dispatched event that the input method didn't handle.
 *    f. (A preDispatchEvent() call on this event will now return false).
 * 3. Application calls finishEvent() with whether it was handled.
 *    - If handled is true, the event is finished.
 *    - If handled is false, the event is put on mUnhandledKeys, and:
 *      a. Main thread receives event from consumeUnhandledEvent().
 *      b. Java sends event through default key handler.
 *      c. event is finished.
 */
struct AInputQueue {
struct AInputQueue : public android::InputEventFactoryInterface {
public:
    /* Creates a consumer associated with an input channel. */
    explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite);
@@ -59,8 +77,9 @@ public:

    int32_t getEvent(AInputEvent** outEvent);

    void finishEvent(AInputEvent* event, bool handled);
    bool preDispatchEvent(AInputEvent* event);

    void finishEvent(AInputEvent* event, bool handled);

    // ----------------------------------------------------------

@@ -68,28 +87,63 @@ public:

    void dispatchEvent(android::KeyEvent* event);

    void finishPreDispatch(int seq, bool handled);

    android::KeyEvent* consumeUnhandledEvent();
    android::KeyEvent* consumePreDispatchingEvent(int* outSeq);

    virtual android::KeyEvent* createKeyEvent();
    virtual android::MotionEvent* createMotionEvent();

    int mWorkWrite;

private:
    void doDefaultKey(android::KeyEvent* keyEvent);
    void doUnhandledKey(android::KeyEvent* keyEvent);
    bool preDispatchKey(android::KeyEvent* keyEvent);
    void wakeupDispatch();

    android::InputConsumer mConsumer;
    android::PreallocatedInputEventFactory mInputEventFactory;
    android::sp<android::PollLoop> mPollLoop;

    int mDispatchKeyRead;
    int mDispatchKeyWrite;

    // This is only touched by the event reader thread.  It is the current
    // key events that came out of the mDispatchingKeys list and are now
    //Êdelivered to the app.
    android::Vector<android::KeyEvent*> mDeliveringKeys;
    struct in_flight_event {
        android::InputEvent* event;
        int seq;
        bool doFinish;
    };

    struct finish_pre_dispatch {
        int seq;
        bool handled;
    };

    android::Mutex mLock;
    android::Vector<android::KeyEvent*> mPendingKeys;

    int mSeq;

    // Cache of previously allocated key events.
    android::Vector<android::KeyEvent*> mAvailKeyEvents;
    // Cache of previously allocated motion events.
    android::Vector<android::MotionEvent*> mAvailMotionEvents;

    // All input events that are actively being processed.
    android::Vector<in_flight_event> mInFlightEvents;

    // Key events that the app didn't handle, and are pending for
    // delivery to the activity's default key handling.
    android::Vector<android::KeyEvent*> mUnhandledKeys;

    // Keys that arrived in the Java framework and need to be
    // dispatched to the app.
    android::Vector<android::KeyEvent*> mDispatchingKeys;

    // Key events that are pending to be pre-dispatched to the IME.
    android::Vector<in_flight_event> mPreDispatchingKeys;

    // Event sequence numbers that we have finished pre-dispatching.
    android::Vector<finish_pre_dispatch> mFinishPreDispatches;
};

#endif // _ANDROID_APP_NATIVEACTIVITY_H
+2 −0
Original line number Diff line number Diff line
@@ -152,6 +152,7 @@ public:
    
protected:
    void initialize(int32_t deviceId, int32_t source);
    void initialize(const InputEvent& from);

private:
    int32_t mDeviceId;
@@ -202,6 +203,7 @@ public:
            int32_t repeatCount,
            nsecs_t downTime,
            nsecs_t eventTime);
    void initialize(const KeyEvent& from);

private:
    int32_t mAction;
+17 −0
Original line number Diff line number Diff line
@@ -18,6 +18,11 @@ void InputEvent::initialize(int32_t deviceId, int32_t source) {
    mSource = source;
}

void InputEvent::initialize(const InputEvent& from) {
    mDeviceId = from.mDeviceId;
    mSource = from.mSource;
}

// class KeyEvent

bool KeyEvent::hasDefaultAction(int32_t keyCode) {
@@ -106,6 +111,18 @@ void KeyEvent::initialize(
    mEventTime = eventTime;
}

void KeyEvent::initialize(const KeyEvent& from) {
    InputEvent::initialize(from);
    mAction = from.mAction;
    mFlags = from.mFlags;
    mKeyCode = from.mKeyCode;
    mScanCode = from.mScanCode;
    mMetaState = from.mMetaState;
    mRepeatCount = from.mRepeatCount;
    mDownTime = from.mDownTime;
    mEventTime = from.mEventTime;
}

// class MotionEvent

void MotionEvent::initialize(
Loading