/** * Copyright 2024 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. */ #pragma once #include #include #include #include #include #include #include #include namespace android { /** * An interface to receive batched input events. Even if you don't want batching, you still have to * use this interface, and some of the events will be batched if your implementation is slow to * handle the incoming input. The events received by these callbacks are never null. */ class InputConsumerCallbacks { public: virtual ~InputConsumerCallbacks(){}; virtual void onKeyEvent(std::unique_ptr event, uint32_t seq) = 0; virtual void onMotionEvent(std::unique_ptr event, uint32_t seq) = 0; /** * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents". * If you don't want batching, then call "consumeBatchedInputEvents" immediately with * std::nullopt requestedFrameTime to receive the pending motion event(s). * @param pendingBatchSource the source of the pending batch. */ virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0; virtual void onFocusEvent(std::unique_ptr event, uint32_t seq) = 0; virtual void onCaptureEvent(std::unique_ptr event, uint32_t seq) = 0; virtual void onDragEvent(std::unique_ptr event, uint32_t seq) = 0; virtual void onTouchModeEvent(std::unique_ptr event, uint32_t seq) = 0; }; /** * Consumes input events from an input channel. * * This is a re-implementation of InputConsumer. At the moment it only supports resampling for * single pointer events. A lot of the higher-level logic has been folded into this class, to make * it easier to use. In the legacy class, InputConsumer, the consumption logic was partially handled * in the jni layer, as well as various actions like adding the fd to the Choreographer. * * TODO(b/297226446): use this instead of "InputConsumer": * - Add resampling for multiple pointer events. * - Allow various resampling strategies to be specified * - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer". * - Add tracing * - Update all tests to use the new InputConsumer * * This class is not thread-safe. We are currently allowing the constructor to run on any thread, * but all of the remaining APIs should be invoked on the looper thread only. */ class InputConsumerNoResampling final { public: /** * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever * the event is ready to consume. * @param looper needs to be sp and not shared_ptr because it inherits from * RefBase * @param resamplerCreator callable that returns the resampling strategy to be used. If null, no * resampling will be performed. resamplerCreator must never return nullptr. */ explicit InputConsumerNoResampling( const std::shared_ptr& channel, sp looper, InputConsumerCallbacks& callbacks, std::function()> resamplerCreator); ~InputConsumerNoResampling(); /** * Must be called exactly once for each event received through the callbacks. */ void finishInputEvent(uint32_t seq, bool handled); void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime); /** * If you want to consume all events immediately (disable batching), then you still must call * this. For requestedFrameTime, use a std::nullopt. It is not guaranteed that the consumption * will occur at requestedFrameTime. The resampling strategy may modify it. * @param requestedFrameTime the time up to which consume the events. When there's double (or * triple) buffering, you may want to not consume all events currently available, because you * could be still working on an older frame, but there could already have been events that * arrived that are more recent. * @return whether any events were actually consumed */ bool consumeBatchedInputEvents(std::optional requestedFrameTime); /** * Returns true when there is *likely* a pending batch or a pending event in the channel. * * This is only a performance hint and may return false negative results. Clients should not * rely on availability of the message based on the return value. */ bool probablyHasInput() const; std::string getName() { return mChannel->getName(); } std::string dump() const; private: std::shared_ptr mChannel; sp mLooper; InputConsumerCallbacks& mCallbacks; const std::function()> mResamplerCreator; /** * A map to manage multidevice resampling. Each contained resampler is never null. This map is * only modified by handleMessages. */ std::map> mResamplers; // Looper-related infrastructure /** * This class is needed to associate the function "handleReceiveCallback" with the provided * looper. The callback sent to the looper is RefBase - based, so we can't just send a reference * of this class directly to the looper. */ class LooperEventCallback : public LooperCallback { public: LooperEventCallback(std::function callback) : mCallback(callback) {} int handleEvent(int /*fd*/, int events, void* /*data*/) override { return mCallback(events); } private: const std::function mCallback; }; sp mCallback; /** * The actual code that executes when the looper encounters available data on the InputChannel. */ int handleReceiveCallback(int events); int mFdEvents; void setFdEvents(int events); void ensureCalledOnLooperThread(const char* func) const; // Event-reading infrastructure /** * A fifo queue of events to be sent to the InputChannel. We can't send all InputMessages to * the channel immediately when they are produced, because it's possible that the InputChannel * is blocked (if the channel buffer is full). When that happens, we don't want to drop the * events. Therefore, events should only be erased from the queue after they've been * successfully written to the InputChannel. */ std::queue mOutboundQueue; /** * Try to send all of the events in mOutboundQueue over the InputChannel. Not all events might * actually get sent, because it's possible that the channel is blocked. */ void processOutboundEvents(); /** * The time at which each event with the sequence number 'seq' was consumed. * This data is provided in 'finishInputEvent' so that the receiving end can measure the latency * This collection is populated when the event is received, and the entries are erased when the * events are finished. It should not grow infinitely because if an event is not ack'd, ANR * will be raised for that connection, and no further events will be posted to that channel. */ std::unordered_map mConsumeTimes; /** * Find and return the consumeTime associated with the provided sequence number. Crashes if * the provided seq number is not found. */ nsecs_t popConsumeTime(uint32_t seq); // Event reading and processing /** * Read all of the available events from the InputChannel */ std::vector readAllMessages(); /** * Send InputMessage to the corresponding InputConsumerCallbacks function. * @param msg */ void handleMessage(const InputMessage& msg) const; // Batching /** * Batch messages that can be batched. When an unbatchable message is encountered, send it * to the InputConsumerCallbacks immediately. If there are batches remaining, * notify InputConsumerCallbacks. If a resampleable ACTION_DOWN message is received, then a * resampler is inserted for that deviceId in mResamplers. If a resampleable ACTION_UP or * ACTION_CANCEL message is received then the resampler associated to that deviceId is erased * from mResamplers. */ void handleMessages(std::vector&& messages); /** * Batched InputMessages, per deviceId. * For each device, we are storing a queue of batched messages. These will all be collapsed into * a single MotionEvent (up to a specific requestedFrameTime) when the consumer calls * `consumeBatchedInputEvents`. */ std::map> mBatches; /** * Creates a MotionEvent by consuming samples from the provided queue. Consumes all messages * with eventTime <= requestedFrameTime - resampleLatency, where `resampleLatency` is latency * introduced by the resampler. Assumes that messages are queued in chronological order. * @param requestedFrameTime The time up to which consume messages, as given by the inequality * above. If std::nullopt, everything in messages will be consumed. * @param messages the queue of messages to consume from. */ std::pair, std::optional> createBatchedMotionEvent( const std::optional requestedFrameTime, std::queue& messages); /** * Consumes the batched input events, optionally allowing the caller to specify a device id * and/or requestedFrameTime threshold. It is not guaranteed that consumption will occur at * requestedFrameTime. * @param deviceId The device id from which to consume events. If std::nullopt, consumes events * from any device id. * @param requestedFrameTime The time up to which consume the events. If std::nullopt, consumes * input events with any timestamp. * @return Whether or not any events were consumed. */ bool consumeBatchedInputEvents(std::optional deviceId, std::optional requestedFrameTime); /** * A map from a single sequence number to several sequence numbers. This is needed because of * batching. When batching is enabled, a single MotionEvent will contain several samples. Each * sample came from an individual InputMessage of Type::Motion, and therefore will have to be * finished individually. Therefore, when the app calls "finish" on a (possibly batched) * MotionEvent, we will need to check this map in case there are multiple sequence numbers * associated with a single number that the app provided. * * For example: * Suppose we received 4 InputMessage's of type Motion, with action MOVE: * InputMessage(MOVE) InputMessage(MOVE) InputMessage(MOVE) InputMessage(MOVE) * seq=10 seq=11 seq=12 seq=13 * The app consumed them all as a batch, which means that the app received a single MotionEvent * with historySize=3 and seq = 10. * * This map will look like: * { * 10: [11, 12, 13], * } * So the sequence number 10 will have 3 other sequence numbers associated with it. * When the app calls 'finish' for seq=10, we need to call 'finish' 4 times total, for sequence * numbers 10, 11, 12, 13. The app is not aware of the sequence numbers of each sample inside * the batched MotionEvent that it received. */ std::map> mBatchedSequenceNumbers; }; } // namespace android