主頁 > 移動端開發 > Android R input (四) 之 InputChannel之發送事件處理反饋

Android R input (四) 之 InputChannel之發送事件處理反饋

2021-10-25 07:15:54 移動端開發

文章托管在gitee上 Android Notes , 同步csdn

Client處理完input事件后, 會向ims發送finish反饋信號,即向對端的server InputChannel發送反饋, server 端InputChannel被InputDispatcher所管理 .這個操作通常在InputEventReceiver的finishInputEvent方法.

InputEventReceiver#finishInputEvent

/// @frameworks/base/core/java/android/view/InputEventReceiver.java
/**
 * Finishes an input event and indicates whether it was handled.
 * Must be called on the same Looper thread to which the receiver is attached.
 *
 * @param event The input event that was finished.
 * @param handled True if the event was handled.
 */
public final void finishInputEvent(InputEvent event, boolean handled) {
    if (event == null) {
        throw new IllegalArgumentException("event must not be null");
    }
    // mReceiverPtr是native的NativeInputEventReceiver地址
    if (mReceiverPtr == 0) {
        Log.w(TAG, "Attempted to finish an input event but the input event "
                + "receiver has already been disposed.");
    } else {
        int index = mSeqMap.indexOfKey(event.getSequenceNumber());
        if (index < 0) {
            Log.w(TAG, "Attempted to finish an input event that is not in progress.");
        } else {
            int seq = mSeqMap.valueAt(index);
            mSeqMap.removeAt(index);
            // 呼叫 native 方法
            nativeFinishInputEvent(mReceiverPtr, seq, handled);
        }
    }
    event.recycleIfNeededAfterDispatch();
}

InputEventReceiver JNI函式注冊表

/// @frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static const JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "nativeInit",
            "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
            (void*)nativeInit },
    { "nativeDispose", "(J)V",
            (void*)nativeDispose },
    { "nativeFinishInputEvent", "(JIZ)V",
            (void*)nativeFinishInputEvent },
    { "nativeConsumeBatchedInputEvents", "(JJ)Z",
            (void*)nativeConsumeBatchedInputEvents },
};

從上可知 nativeFinishInputEvent方法對應的jni函式是android_view_InputEventReceiver.cpp中的nativeFinishInputEvent函式

static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jlong receiverPtr,
        jint seq, jboolean handled) {
    sp<NativeInputEventReceiver> receiver =
            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
      // 呼叫NativeInputEventReceiver的finishInputEvent
    status_t status = receiver->finishInputEvent(seq, handled);
    if (status && status != DEAD_OBJECT) {
        String8 message;
        message.appendFormat("Failed to finish input event.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
    }
}

NativeInputEventReceiver::finishInputEvent

status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
    if (kDebugDispatchCycle) {
        ALOGD("channel '%s' ~ Finished input event.", getInputChannelName().c_str());
    }
    // 通過mInputConsumer發送finish信號
    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
    if (status) {
        if (status == WOULD_BLOCK) {// 此時處于阻塞狀態,添加到mFinishQueue
            if (kDebugDispatchCycle) {
                ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
                        "Enqueued for later.", getInputChannelName().c_str());
            }
            Finish finish;
            finish.seq = seq;
            finish.handled = handled;
            mFinishQueue.add(finish);
            // 從0 -> 1 時設定ALOOPER_EVENT_OUTPUT=1事件監聽, 當fd可寫時收到相關事件,回呼NativeInputEventReceiver::handleEvent,
            // 然后遍歷mFinishQueue中的元素,取出繼續發送pending的事件反饋, 所有發送完成后清除佇列,并移除ALOOPER_EVENT_OUTPUT監聽
            if (mFinishQueue.size() == 1) {
                setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
            }
            return OK;
        }
        ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                getInputChannelName().c_str(), status);
    }
    return status;
}

InputConsumer::sendFinishedSignal

status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
    if (DEBUG_TRANSPORT_ACTIONS) {
        ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
              mChannel->getName().c_str(), seq, toString(handled));
    }

    if (!seq) { // seq不能是0
        ALOGE("Attempted to send a finished signal with sequence number 0.");
        return BAD_VALUE;
    }
    // 先處理 batch sequence chain
    // Send finished signals for the batch sequence chain first.
    size_t seqChainCount = mSeqChains.size();
    if (seqChainCount) {
        uint32_t currentSeq = seq;
        uint32_t chainSeqs[seqChainCount];
        size_t chainIndex = 0;
        for (size_t i = seqChainCount; i > 0; ) {
             i--;
             const SeqChain& seqChain = mSeqChains.itemAt(i);
             if (seqChain.seq == currentSeq) {
                 currentSeq = seqChain.chain;
                 chainSeqs[chainIndex++] = currentSeq;
                 mSeqChains.removeAt(i);
             }
        }
        status_t status = OK;
        while (!status && chainIndex > 0) {
            chainIndex--;
            status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
        }
        if (status) {
            // An error occurred so at least one signal was not sent, reconstruct the chain.
            for (;;) {
                SeqChain seqChain;
                seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
                seqChain.chain = chainSeqs[chainIndex];
                mSeqChains.push(seqChain);
                if (!chainIndex) break;
                chainIndex--;
            }
            return status;
        }
    }

    // Send finished signal for the last message in the batch.
    // 發送最后一條
    return sendUnchainedFinishedSignal(seq, handled);
}

InputConsumer::sendUnchainedFinishedSignal

status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
    InputMessage msg;
    msg.header.type = InputMessage::Type::FINISHED;
    msg.body.finished.seq = seq;
    msg.body.finished.handled = handled ? 1 : 0;
    return mChannel->sendMessage(&msg);
}

InputChannel::sendMessage

status_t InputChannel::sendMessage(const InputMessage* msg) {
    const size_t msgLength = msg->size();
    InputMessage cleanMsg;
    msg->getSanitizedCopy(&cleanMsg);
    ssize_t nWrite;
    do {
      // 呼叫send 函式, send a message on a socket
      // ssize_t send(int sockfd, const void *buf, size_t len, int flags);
        nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite < 0) { // 發送的長度<0 發送失敗
        int error = errno;
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
              msg->header.type, strerror(error));
#endif
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (size_t(nWrite) != msgLength) { // 發送長度和實際的不一致
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ error sending message type %d, send was incomplete",
                mName.c_str(), msg->header.type);
#endif
        return DEAD_OBJECT;
    }

#if DEBUG_CHANNEL_MESSAGES
    ALOGD("channel '%s' ~ sent message of type %d", mName.c_str(), msg->header.type);
#endif
    return OK;
}

接下來看Server端的處理.Server端通常是管理在inputDispatcher. 當收到新訊息到來,會觸發ims的Looper處理相關fd事件, 之后InputDispatcher::handleReceiveCallback會被回呼

InputDispatcher::handleReceiveCallback

int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
    InputDispatcher* d = static_cast<InputDispatcher*>(data);

    { // acquire lock
        std::scoped_lock _l(d->mLock);
        // 尋找fd對應的Connection
        if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
            ALOGE("Received spurious receive callback for unknown input channel.  "
                  "fd=%d, events=0x%x",
                  fd, events);
            return 0; // remove the callback
        }

        bool notify;
        sp<Connection> connection = d->mConnectionsByFd[fd];
        if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
            if (!(events & ALOOPER_EVENT_INPUT)) { // 非input事件則回傳
                ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
                      "events=0x%x",
                      connection->getInputChannelName().c_str(), events);
                return 1;
            }

            nsecs_t currentTime = now();
            bool gotOne = false;
            status_t status;
            for (;;) {
                uint32_t seq;
                bool handled;
                // 接收client的finish訊息
                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
                if (status) {
                    break;
                }
                // 結束派發反饋的cycle, 將要執行的操作封裝成一個CommandEntry,添加到mCommandQueue
                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
                gotOne = true;
            }
            // gotOne為true , 則必然成功收到了client的finish訊息, 執行mCommandQueue中的commands
            if (gotOne) {
                d->runCommandsLockedInterruptible();
                if (status == WOULD_BLOCK) {
                    return 1;
                }
            }

            notify = status != DEAD_OBJECT || !connection->monitor;
            if (notify) {
                ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                      connection->getInputChannelName().c_str(), status);
            }
        } else {
            // Monitor channels are never explicitly unregistered.
            // We do it automatically when the remote endpoint is closed so don't warn
            // about them.
            const bool stillHaveWindowHandle =
                    d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) !=
                    nullptr;
            notify = !connection->monitor && stillHaveWindowHandle;
            if (notify) {
                ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                      "events=0x%x",
                      connection->getInputChannelName().c_str(), events);
            }
        }

        // Unregister the channel.
        d->unregisterInputChannelLocked(connection->inputChannel, notify);
        return 0; // remove the callback
    }             // release lock
}

InputPublisher::receiveFinishedSignal

接收來自client的finish 資訊

status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
    if (DEBUG_TRANSPORT_ACTIONS) {
        ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
    }

    InputMessage msg;
    // 呼叫InputChannel的receiveMessage讀取msg
    status_t result = mChannel->receiveMessage(&msg);
    if (result) {
        *outSeq = 0;
        *outHandled = false;
        return result;
    }
    if (msg.header.type != InputMessage::Type::FINISHED) {
        ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
                mChannel->getName().c_str(), msg.header.type);
        return UNKNOWN_ERROR;
    }
    *outSeq = msg.body.finished.seq;
    *outHandled = msg.body.finished.handled == 1; // 判斷是否handled, 為1則handled
    return OK;
}

InputChannel::receiveMessage

status_t InputChannel::receiveMessage(InputMessage* msg) {
    ssize_t nRead;
    do {
      // 讀取到msg
        nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
    } while (nRead == -1 && errno == EINTR);

    if (nRead < 0) {
        int error = errno;
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.c_str(), errno);
#endif
        if (error == EAGAIN || error == EWOULDBLOCK) {
            return WOULD_BLOCK;
        }
        if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
            return DEAD_OBJECT;
        }
        return -error;
    }

    if (nRead == 0) { // check for EOF
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.c_str());
#endif
        return DEAD_OBJECT;
    }

    if (!msg->isValid(nRead)) {
#if DEBUG_CHANNEL_MESSAGES
        ALOGD("channel '%s' ~ received invalid message", mName.c_str());
#endif
        return BAD_VALUE;
    }

#if DEBUG_CHANNEL_MESSAGES
    ALOGD("channel '%s' ~ received message of type %d", mName.c_str(), msg->header.type);
#endif
    return OK;
}

InputDispatcher::finishDispatchCycleLocked

void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                const sp<Connection>& connection, uint32_t seq,
                                                bool handled) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s",
          connection->getInputChannelName().c_str(), seq, toString(handled));
#endif

    if (connection->status == Connection::STATUS_BROKEN ||
        connection->status == Connection::STATUS_ZOMBIE) {
        return;
    }

    // Notify other system components and prepare to start the next dispatch cycle.
    // 結束當前事件回圈, 準備執行下一個派發回圈
    onDispatchCycleFinishedLocked(currentTime, connection, seq, handled);
}

InputDispatcher::onDispatchCycleFinishedLocked

void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
                                                    const sp<Connection>& connection, uint32_t seq,
                                                    bool handled) {
    // 創建 CommandEntry, 它的command是類成員 InputDispatcher::doDispatchCycleFinishedLockedInterruptible
    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
            &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
    commandEntry->connection = connection;
    commandEntry->eventTime = currentTime;  // 事件處理結束時間
    commandEntry->seq = seq;
    commandEntry->handled = handled;
    postCommandLocked(std::move(commandEntry));
}

// 添加到 mCommandQueue
void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) {
    mCommandQueue.push_back(std::move(commandEntry));
}


接下來看看CommandEntry的定義:

/// @frameworks/native/services/inputflinger/dispatcher/Entry.h
class InputDispatcher;
// A command entry captures state and behavior for an action to be performed in the
// dispatch loop after the initial processing has taken place.  It is essentially
// a kind of continuation used to postpone sensitive policy interactions to a point
// in the dispatch loop where it is safe to release the lock (generally after finishing
// the critical parts of the dispatch cycle).
//
// The special thing about commands is that they can voluntarily release and reacquire
// the dispatcher lock at will.  Initially when the command starts running, the
// dispatcher lock is held.  However, if the command needs to call into the policy to
// do some work, it can release the lock, do the work, then reacquire the lock again
// before returning.
//
// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch
// never calls into the policy while holding its lock.
//
// Commands are implicitly 'LockedInterruptible'.
struct CommandEntry;
// Command 的定義, 本質上是函式指標. 對于InputDispatcher類成員函式而言,第一個引數即是InputDispatcher自身
typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;

class Connection;
struct CommandEntry {
    explicit CommandEntry(Command command);
    ~CommandEntry();

    Command command;

    // parameters for the command (usage varies by command)
    sp<Connection> connection;
    nsecs_t eventTime;
    KeyEntry* keyEntry;
    sp<InputApplicationHandle> inputApplicationHandle;
    std::string reason;
    int32_t userActivityEventType;
    uint32_t seq;
    bool handled;
    sp<InputChannel> inputChannel;
    sp<IBinder> oldToken;
    sp<IBinder> newToken;
};

看看對Command注釋的翻譯:

初始處理完成后,命令條目將捕獲要在調度回圈中執行的操作的狀態和行為, 從本質上講,這是一種延續,用于將敏感的策略互動延遲到分發回圈中能安全釋放鎖的某個點(通常在完成分發周期的關鍵部分之后),

關于命令的特殊之處在于它們可以隨意釋放并重新獲取調度程式鎖, 最初,當命令開始運行時,將保持調度程式鎖定, 但是,如果命令需要呼叫策略以執行某些作業,則可以釋放該鎖,執行該作業,然后在回傳之前再次重新獲取該鎖,

這種機制有點笨拙,但有助于保留不變性:在分發保持其鎖定狀態時永遠不會呼叫策略,

再次回到 InputDispatcher::handleReceiveCallback , 當執行InputDispatcher#finishDispatchCycleLocked后,會將要執行的操作封裝成CommandEntry并添加到CommandQueue. 接下來執行InputDispatcher#runCommandsLockedInterruptible方法執行command

InputDispatcher#runCommandsLockedInterruptible

bool InputDispatcher::runCommandsLockedInterruptible() {
    if (mCommandQueue.empty()) {
        return false;
    }

    do {
      // 從mCommandQueu取出CommandEntry, 然后執行其Command
        std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
        mCommandQueue.pop_front();
        Command command = commandEntry->command;
        command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'

        commandEntry->connection.clear();
    } while (!mCommandQueue.empty());
    return true;
}

InputDispatcher::doDispatchCycleFinishedLockedInterruptible

對于onDispatchCycleFinishedLocked方法中添加的CommandEntry而言,執行command則會呼叫InputDispatcher::doDispatchCycleFinishedLockedInterruptible方法

void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
    sp<Connection> connection = commandEntry->connection;
    const nsecs_t finishTime = commandEntry->eventTime;
    uint32_t seq = commandEntry->seq;
    const bool handled = commandEntry->handled;

    // Handle post-event policy actions.
    // 獲取WaitQueue中seq對于的條目
    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
    if (dispatchEntryIt == connection->waitQueue.end()) {
        return;
    }
    DispatchEntry* dispatchEntry = *dispatchEntryIt;
    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { // 事件處理超時
        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
    }
    // TODO Write some statistics about how long we spend waiting.
    reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled);

    // 判斷是否需要重新派發此事件
    bool restartEvent;
    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) { // key事件
        KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
        // 處理key事件未處理的情況, 回傳true則此事件會重新派發
        restartEvent =
                afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) { // 觸摸事件
        MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
        restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
                                                           handled);
    } else {
        restartEvent = false;
    }

    // Dequeue the event and start the next cycle.
    // Because the lock might have been released, it is possible that the
    // contents of the wait queue to have been drained, so we need to double-check
    // a few things.
    dispatchEntryIt = connection->findWaitQueueEntry(seq); // 重新獲取一次,防止wait queue變化
    if (dispatchEntryIt != connection->waitQueue.end()) {
        dispatchEntry = *dispatchEntryIt;
        connection->waitQueue.erase(dispatchEntryIt);  // 將此entry從waitQueue移除
        mAnrTracker.erase(dispatchEntry->timeoutTime,    // 將此token從mAnrTracker移除
                          connection->inputChannel->getConnectionToken());
        if (!connection->responsive) {  // 更新connection的responsive狀態
            connection->responsive = isConnectionResponsive(*connection);
        }
        traceWaitQueueLength(connection);
        // 如果需要重新派發, 則將此entry添加到outboundQueue的佇列頭,待下一次派發
        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
            connection->outboundQueue.push_front(dispatchEntry);
            traceOutboundQueueLength(connection);
        } else { // 否則釋放此entry
            releaseDispatchEntry(dispatchEntry);
        }
    }

    // 執行下一個派發回圈
    // Start the next dispatch cycle for this connection.
    startDispatchCycleLocked(now(), connection);
}

InputDispatcher::afterKeyEventLockedInterruptible

處理unhandled key

bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
                                                       DispatchEntry* dispatchEntry,
                                                       KeyEntry* keyEntry, bool handled) {
     // 若此keyEntry已經有fallback標志,則回傳以防事件持續未處理 , 以防反復呼叫  
    if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
        if (!handled) {
            // Report the key as unhandled, since the fallback was not handled.
            mReporter->reportUnhandledKey(keyEntry->id);
        }
        return false;
    }

    // Get the fallback key state.
    // Clear it out after dispatching the UP.
    int32_t originalKeyCode = keyEntry->keyCode;
    int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
    if (keyEntry->action == AKEY_EVENT_ACTION_UP) { // 若是up事件,則移除FallbackKey
        connection->inputState.removeFallbackKey(originalKeyCode);
    }

    if (handled || !dispatchEntry->hasForegroundTarget()) {
        // If the application handles the original key for which we previously
        // generated a fallback or if the window is not a foreground window,
        // then cancel the associated fallback key, if any.
        if (fallbackKeyCode != -1) { // 如果應用處理了原始key或者window不是前臺的,則取消fallback key
            // Dispatch the unhandled key to the policy with the cancel flag.
#if DEBUG_OUTBOUND_EVENT_DETAILS
            ALOGD("Unhandled key event: Asking policy to cancel fallback action.  "
                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
                  keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
                  keyEntry->policyFlags);
#endif
            KeyEvent event = createKeyEvent(*keyEntry);
            event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);

            mLock.unlock();

            mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
                                          keyEntry->policyFlags, &event);

            mLock.lock();

            // Cancel the fallback key.
            if (fallbackKeyCode != AKEYCODE_UNKNOWN) {
                CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
                                           "application handled the original non-fallback key "
                                           "or is no longer a foreground target, "
                                           "canceling previously dispatched fallback key");
                options.keyCode = fallbackKeyCode;
                synthesizeCancelationEventsForConnectionLocked(connection, options);
            }
            connection->inputState.removeFallbackKey(originalKeyCode);
        }
    } else {
      // 處理非fallback key 未處理的情況
        // If the application did not handle a non-fallback key, first check
        // that we are in a good state to perform unhandled key event processing
        // Then ask the policy what to do with it.
        bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0;
        if (fallbackKeyCode == -1 && !initialDown) {// 還沒設定fallbackKeyCode且不是初始down事件
#if DEBUG_OUTBOUND_EVENT_DETAILS
            ALOGD("Unhandled key event: Skipping unhandled key event processing "
                  "since this is not an initial down.  "
                  "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
                  originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
#endif
            return false;
        }

        // 通過策略派發未處理key事件
        // Dispatch the unhandled key to the policy.
#if DEBUG_OUTBOUND_EVENT_DETAILS
        ALOGD("Unhandled key event: Asking policy to perform fallback action.  "
              "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
              keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
#endif
        KeyEvent event = createKeyEvent(*keyEntry);

        mLock.unlock();

        bool fallback =
                mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
                                              &event, keyEntry->policyFlags, &event);

        mLock.lock();

        if (connection->status != Connection::STATUS_NORMAL) {
            connection->inputState.removeFallbackKey(originalKeyCode);
            return false;
        }

        // Latch the fallback keycode for this key on an initial down.
        // The fallback keycode cannot change at any other point in the lifecycle.
        if (initialDown) {
            if (fallback) {
                fallbackKeyCode = event.getKeyCode();
            } else {
                fallbackKeyCode = AKEYCODE_UNKNOWN;
            }
            connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
        }

        ALOG_ASSERT(fallbackKeyCode != -1);

        // Cancel the fallback key if the policy decides not to send it anymore.
        // We will continue to dispatch the key to the policy but we will no
        // longer dispatch a fallback key to the application.
        if (fallbackKeyCode != AKEYCODE_UNKNOWN &&
            (!fallback || fallbackKeyCode != event.getKeyCode())) { // 處理keyCode不一致的情況
#if DEBUG_OUTBOUND_EVENT_DETAILS
            if (fallback) {
                ALOGD("Unhandled key event: Policy requested to send key %d"
                      "as a fallback for %d, but on the DOWN it had requested "
                      "to send %d instead.  Fallback canceled.",
                      event.getKeyCode(), originalKeyCode, fallbackKeyCode);
            } else {
                ALOGD("Unhandled key event: Policy did not request fallback for %d, "
                      "but on the DOWN it had requested to send %d.  "
                      "Fallback canceled.",
                      originalKeyCode, fallbackKeyCode);
            }
#endif

            CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS,
                                       "canceling fallback, policy no longer desires it");
            options.keyCode = fallbackKeyCode;
            synthesizeCancelationEventsForConnectionLocked(connection, options);

            fallback = false;
            fallbackKeyCode = AKEYCODE_UNKNOWN;
            if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
                connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
            }
        }

        if (fallback) { /// 初始化fallback keyEntry
            // Restart the dispatch cycle using the fallback key.
            keyEntry->eventTime = event.getEventTime();
            keyEntry->deviceId = event.getDeviceId();
            keyEntry->source = event.getSource();
            keyEntry->displayId = event.getDisplayId();
            keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
            keyEntry->keyCode = fallbackKeyCode;
            keyEntry->scanCode = event.getScanCode();
            keyEntry->metaState = event.getMetaState();
            keyEntry->repeatCount = event.getRepeatCount();
            keyEntry->downTime = event.getDownTime();
            keyEntry->syntheticRepeat = false;

#if DEBUG_OUTBOUND_EVENT_DETAILS
            ALOGD("Unhandled key event: Dispatching fallback key.  "
                  "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
                  originalKeyCode, fallbackKeyCode, keyEntry->metaState);
#endif
            return true; // restart the event
        } else {
#if DEBUG_OUTBOUND_EVENT_DETAILS
            ALOGD("Unhandled key event: No fallback key.");
#endif

            // Report the key as unhandled, since there is no fallback key.
            mReporter->reportUnhandledKey(keyEntry->id);
        }
    }
    return false;
}

InputDispatcher::startDispatchCycleLocked

執行新一輪的派發

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
                                               const sp<Connection>& connection) {
    if (ATRACE_ENABLED()) {
        std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)",
                                           connection->getInputChannelName().c_str());
        ATRACE_NAME(message.c_str());
    }
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
#endif

    while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.front();
        dispatchEntry->deliveryTime = currentTime;
        const nsecs_t timeout =
                getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
        dispatchEntry->timeoutTime = currentTime + timeout;

        // Publish the event.
        status_t status;
        EventEntry* eventEntry = dispatchEntry->eventEntry;
        switch (eventEntry->type) {
            case EventEntry::Type::KEY: {  // 派發key事件
                const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
                std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);

                // Publish the key event.
                status =
                        connection->inputPublisher
                                .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
                                                 keyEntry->deviceId, keyEntry->source,
                                                 keyEntry->displayId, std::move(hmac),
                                                 dispatchEntry->resolvedAction,
                                                 dispatchEntry->resolvedFlags, keyEntry->keyCode,
                                                 keyEntry->scanCode, keyEntry->metaState,
                                                 keyEntry->repeatCount, keyEntry->downTime,
                                                 keyEntry->eventTime);
                break;
            }

            case EventEntry::Type::MOTION: {
                ...
                // Publish the motion event.
                status = connection->inputPublisher.publishMotionEvent(...)
                break;
            }
            case EventEntry::Type::FOCUS: {
                FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry);
                status = connection->inputPublisher.publishFocusEvent(...);
                break;
            }

            case EventEntry::Type::CONFIGURATION_CHANGED:
            case EventEntry::Type::DEVICE_RESET: {
                LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
                                 EventEntry::typeToString(eventEntry->type));
                return;
            }
        }

        // Check the result.
        if (status) {
            if (status == WOULD_BLOCK) {
                if (connection->waitQueue.empty()) {
                    ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
                          "This is unexpected because the wait queue is empty, so the pipe "
                          "should be empty and we shouldn't have any problems writing an "
                          "event to it, status=%d",
                          connection->getInputChannelName().c_str(), status);
                    abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
                } else {
                    // Pipe is full and we are waiting for the app to finish process some events
                    // before sending more events to it.
#if DEBUG_DISPATCH_CYCLE
                    ALOGD("channel '%s' ~ Could not publish event because the pipe is full, "
                          "waiting for the application to catch up",
                          connection->getInputChannelName().c_str());
#endif
                }
            } else {
                ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
                      "status=%d",
                      connection->getInputChannelName().c_str(), status);
                abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);
            }
            return;
        }

        // 將已派發的dispatchEntry從outboundQueue移除
        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
                                                    connection->outboundQueue.end(),
                                                    dispatchEntry));
        traceOutboundQueueLength(connection);
        // 添加dispatchEntry到waitQueue, 等待client的finish反饋
        connection->waitQueue.push_back(dispatchEntry);
        if (connection->responsive) { // 設定anr檢測
            mAnrTracker.insert(dispatchEntry->timeoutTime,
                               connection->inputChannel->getConnectionToken());
        }
        traceWaitQueueLength(connection);
    }
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/335188.html

標籤:其他

上一篇:Vue-Axios請封裝

下一篇:手機訊息推送方案綜述

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more