diff options
author | 2024-08-12 20:21:13 +0000 | |
---|---|---|
committer | 2024-09-10 21:01:23 +0000 | |
commit | e2bb18735b214e773c0039581a1fa1771f442c71 (patch) | |
tree | 44de852b9e8ef06dc5d9afe8c19f36d310b0f8c8 | |
parent | 81ba7fb7ec8356221d147c8313913ae7dc792a44 (diff) |
Create TestInputChannel and TestLooper to better test InputConsumerNoResampling
Created TestInputChannel and TestLooper to control how InputMessages are
batched in InputConsumerNoResampling_test
Bug: 297226446
Flag: EXEMPT refactor
Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="InputConsumerTest*"
Change-Id: I036593b311a93ec301e8b9776b461505e853119f
-rw-r--r-- | include/input/InputConsumerNoResampling.h | 13 | ||||
-rw-r--r-- | include/input/InputTransport.h | 11 | ||||
-rw-r--r-- | include/input/LooperInterface.h | 39 | ||||
-rw-r--r-- | libs/input/InputConsumerNoResampling.cpp | 44 | ||||
-rw-r--r-- | libs/input/tests/Android.bp | 3 | ||||
-rw-r--r-- | libs/input/tests/InputConsumer_test.cpp | 123 | ||||
-rw-r--r-- | libs/input/tests/TestInputChannel.cpp | 85 | ||||
-rw-r--r-- | libs/input/tests/TestInputChannel.h | 66 | ||||
-rw-r--r-- | libs/input/tests/TestLooper.cpp | 51 | ||||
-rw-r--r-- | libs/input/tests/TestLooper.h | 56 |
10 files changed, 475 insertions, 16 deletions
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index 65c2914b3c..358a19158e 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -17,6 +17,7 @@ #pragma once #include <input/InputTransport.h> +#include <input/LooperInterface.h> #include <input/Resampler.h> #include <utils/Looper.h> @@ -66,6 +67,16 @@ public: class InputConsumerNoResampling final { public: /** + * This constructor is exclusively for test code. Any real use of InputConsumerNoResampling must + * use the constructor that takes an sp<Looper> parameter instead of + * std::shared_ptr<LooperInterface>. + */ + explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, + std::shared_ptr<LooperInterface> looper, + InputConsumerCallbacks& callbacks, + std::unique_ptr<Resampler> resampler); + + /** * @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 @@ -108,7 +119,7 @@ public: private: std::shared_ptr<InputChannel> mChannel; - sp<Looper> mLooper; + std::shared_ptr<LooperInterface> mLooper; InputConsumerCallbacks& mCallbacks; std::unique_ptr<Resampler> mResampler; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 7d11f76c85..0cd87201fb 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -263,7 +263,7 @@ public: * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t sendMessage(const InputMessage* msg); + virtual status_t sendMessage(const InputMessage* msg); /* Receive a message sent by the other endpoint. * @@ -275,14 +275,14 @@ public: * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - android::base::Result<InputMessage> receiveMessage(); + virtual android::base::Result<InputMessage> receiveMessage(); /* Tells whether there is a message in the channel available to be received. * * 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; + virtual bool probablyHasInput() const; /* Wait until there is a message in the channel. * @@ -323,11 +323,12 @@ public: */ sp<IBinder> getConnectionToken() const; +protected: + InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); + private: static std::unique_ptr<InputChannel> create(const std::string& name, android::base::unique_fd fd, sp<IBinder> token); - - InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); }; /* diff --git a/include/input/LooperInterface.h b/include/input/LooperInterface.h new file mode 100644 index 0000000000..2d6719c965 --- /dev/null +++ b/include/input/LooperInterface.h @@ -0,0 +1,39 @@ +/** + * 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 <utils/Looper.h> +#include <utils/StrongPointer.h> + +namespace android { + +/** + * LooperInterface allows the use of TestLooper in InputConsumerNoResampling without reassigning to + * Looper. LooperInterface is needed to control how InputConsumerNoResampling consumes and batches + * InputMessages. + */ +class LooperInterface { +public: + virtual ~LooperInterface() = default; + + virtual int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, + void* data) = 0; + virtual int removeFd(int fd) = 0; + + virtual sp<Looper> getLooper() const = 0; +}; +} // namespace android diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index 99ffa683dd..eb419180e7 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "InputTransport" +#define LOG_TAG "InputConsumerNoResampling" #define ATRACE_TAG ATRACE_TAG_INPUT #include <chrono> @@ -33,8 +33,6 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> -namespace input_flags = com::android::input::flags; - namespace android { namespace { @@ -46,6 +44,27 @@ namespace { const bool DEBUG_TRANSPORT_CONSUMER = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO); +/** + * RealLooper is a wrapper of Looper. All the member functions exclusively call the internal looper. + * This class' behavior is the same as Looper. + */ +class RealLooper final : public LooperInterface { +public: + RealLooper(sp<Looper> looper) : mLooper{looper} {} + + int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, + void* data) override { + return mLooper->addFd(fd, ident, events, callback, data); + } + + int removeFd(int fd) override { return mLooper->removeFd(fd); } + + sp<Looper> getLooper() const override { return mLooper; } + +private: + sp<Looper> mLooper; +}; + std::unique_ptr<KeyEvent> createKeyEvent(const InputMessage& msg) { std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>(); event->initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source, @@ -173,22 +192,20 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim bool isPointerEvent(const MotionEvent& motionEvent) { return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; } - } // namespace using android::base::Result; -using android::base::StringPrintf; // --- InputConsumerNoResampling --- InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, - sp<Looper> looper, + std::shared_ptr<LooperInterface> looper, InputConsumerCallbacks& callbacks, std::unique_ptr<Resampler> resampler) - : mChannel(channel), - mLooper(looper), + : mChannel{channel}, + mLooper{looper}, mCallbacks(callbacks), - mResampler(std::move(resampler)), + mResampler{std::move(resampler)}, mFdEvents(0) { LOG_ALWAYS_FATAL_IF(mLooper == nullptr); mCallback = sp<LooperEventCallback>::make( @@ -199,6 +216,13 @@ InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<Input setFdEvents(ALOOPER_EVENT_INPUT); } +InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel, + sp<Looper> looper, + InputConsumerCallbacks& callbacks, + std::unique_ptr<Resampler> resampler) + : InputConsumerNoResampling(channel, std::make_shared<RealLooper>(looper), callbacks, + std::move(resampler)) {} + InputConsumerNoResampling::~InputConsumerNoResampling() { ensureCalledOnLooperThread(__func__); consumeBatchedInputEvents(std::nullopt); @@ -513,7 +537,7 @@ bool InputConsumerNoResampling::consumeBatchedInputEvents( void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const { sp<Looper> callingThreadLooper = Looper::getForThread(); - if (callingThreadLooper != mLooper) { + if (callingThreadLooper != mLooper->getLooper()) { LOG(FATAL) << "The function " << func << " can only be called on the looper thread"; } } diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 132866bd99..43bc8948dd 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -16,6 +16,7 @@ cc_test { "BlockingQueue_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", + "InputConsumer_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", @@ -25,6 +26,8 @@ cc_test { "MotionPredictorMetricsManager_test.cpp", "Resampler_test.cpp", "RingBuffer_test.cpp", + "TestInputChannel.cpp", + "TestLooper.cpp", "TfLiteMotionPredictor_test.cpp", "TouchResampling_test.cpp", "TouchVideoFrame_test.cpp", diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp new file mode 100644 index 0000000000..c30f243398 --- /dev/null +++ b/libs/input/tests/InputConsumer_test.cpp @@ -0,0 +1,123 @@ +/** + * 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. + */ + +#include <input/InputConsumerNoResampling.h> + +#include <memory> +#include <optional> +#include <utility> + +#include <TestInputChannel.h> +#include <TestLooper.h> +#include <android-base/logging.h> +#include <gtest/gtest.h> +#include <input/BlockingQueue.h> +#include <input/InputEventBuilders.h> +#include <utils/StrongPointer.h> + +namespace android { + +class InputConsumerTest : public testing::Test, public InputConsumerCallbacks { +protected: + InputConsumerTest() + : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")}, + mTestLooper{std::make_shared<TestLooper>()} { + Looper::setForThread(mTestLooper->getLooper()); + mConsumer = std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mTestLooper, + *this, /*resampler=*/nullptr); + } + + void assertOnBatchedInputEventPendingWasCalled(); + + std::shared_ptr<TestInputChannel> mClientTestChannel; + std::shared_ptr<TestLooper> mTestLooper; + std::unique_ptr<InputConsumerNoResampling> mConsumer; + + BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents; + BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents; + BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents; + BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents; + BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents; + BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents; + +private: + size_t onBatchedInputEventPendingInvocationCount{0}; + + // InputConsumerCallbacks interface + void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override { + mKeyEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override { + mMotionEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onBatchedInputEventPending(int32_t pendingBatchSource) override { + if (!mConsumer->probablyHasInput()) { + ADD_FAILURE() << "should deterministically have input because there is a batch"; + } + ++onBatchedInputEventPendingInvocationCount; + }; + void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override { + mFocusEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; + void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override { + mCaptureEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; + void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override { + mDragEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override { + mTouchModeEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; +}; + +void InputConsumerTest::assertOnBatchedInputEventPendingWasCalled() { + ASSERT_GT(onBatchedInputEventPendingInvocationCount, 0UL) + << "onBatchedInputEventPending has not been called."; + --onBatchedInputEventPendingInvocationCount; +} + +TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) { + mClientTestChannel->enqueueMessage( + InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.build()); + mClientTestChannel->enqueueMessage( + InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.build()); + mClientTestChannel->enqueueMessage( + InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}.build()); + + mClientTestChannel->assertNoSentMessages(); + + mTestLooper->invokeCallback(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT); + + assertOnBatchedInputEventPendingWasCalled(); + + mConsumer->consumeBatchedInputEvents(std::nullopt); + + std::unique_ptr<MotionEvent> batchedMotionEvent = mMotionEvents.pop(); + ASSERT_NE(batchedMotionEvent, nullptr); + + mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); + + EXPECT_EQ(batchedMotionEvent->getHistorySize() + 1, 3UL); +} +} // namespace android diff --git a/libs/input/tests/TestInputChannel.cpp b/libs/input/tests/TestInputChannel.cpp new file mode 100644 index 0000000000..d5f00b699b --- /dev/null +++ b/libs/input/tests/TestInputChannel.cpp @@ -0,0 +1,85 @@ +/** + * 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. + */ + +#define LOG_TAG "TestInputChannel" +#define ATRACE_TAG ATRACE_TAG_INPUT + +#include <TestInputChannel.h> + +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <utils/StrongPointer.h> + +namespace android { + +namespace { +constexpr int FAKE_FD{-1}; +} // namespace + +// --- TestInputChannel --- + +TestInputChannel::TestInputChannel(const std::string& name) + : InputChannel{name, base::unique_fd(FAKE_FD), sp<BBinder>::make()} {} + +void TestInputChannel::enqueueMessage(const InputMessage& message) { + mReceivedMessages.push(message); +} + +status_t TestInputChannel::sendMessage(const InputMessage* message) { + LOG_IF(FATAL, message == nullptr) + << "TestInputChannel " << getName() << ". No message was passed to sendMessage."; + + mSentMessages.push(*message); + return OK; +} + +base::Result<InputMessage> TestInputChannel::receiveMessage() { + if (mReceivedMessages.empty()) { + return base::Error(WOULD_BLOCK); + } + InputMessage message = mReceivedMessages.front(); + mReceivedMessages.pop(); + return message; +} + +bool TestInputChannel::probablyHasInput() const { + return !mReceivedMessages.empty(); +} + +void TestInputChannel::assertFinishMessage(uint32_t seq, bool handled) { + ASSERT_FALSE(mSentMessages.empty()) + << "TestInputChannel " << getName() << ". Cannot assert. mSentMessages is empty."; + + const InputMessage& finishMessage = mSentMessages.front(); + + EXPECT_EQ(finishMessage.header.seq, seq) + << "TestInputChannel " << getName() + << ". Sequence mismatch. Message seq: " << finishMessage.header.seq + << " Expected seq: " << seq; + + EXPECT_EQ(finishMessage.body.finished.handled, handled) + << "TestInputChannel " << getName() + << ". Handled value mismatch. Message val: " << std::boolalpha + << finishMessage.body.finished.handled << "Expected val: " << handled + << std::noboolalpha; + mSentMessages.pop(); +} + +void TestInputChannel::assertNoSentMessages() const { + ASSERT_TRUE(mSentMessages.empty()); +} +} // namespace android
\ No newline at end of file diff --git a/libs/input/tests/TestInputChannel.h b/libs/input/tests/TestInputChannel.h new file mode 100644 index 0000000000..43253ec0ef --- /dev/null +++ b/libs/input/tests/TestInputChannel.h @@ -0,0 +1,66 @@ +/** + * 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 <queue> +#include <string> + +#include <android-base/result.h> +#include <gtest/gtest.h> +#include <input/InputTransport.h> +#include <utils/Errors.h> + +namespace android { + +class TestInputChannel final : public InputChannel { +public: + explicit TestInputChannel(const std::string& name); + + /** + * Enqueues a message in mReceivedMessages. + */ + void enqueueMessage(const InputMessage& message); + + /** + * Pushes message to mSentMessages. In the default implementation, InputChannel sends messages + * through a file descriptor. TestInputChannel, on the contrary, stores sent messages in + * mSentMessages for assertion reasons. + */ + status_t sendMessage(const InputMessage* message) override; + + /** + * Returns an InputMessage from mReceivedMessages. This is done instead of retrieving data + * directly from fd. + */ + base::Result<InputMessage> receiveMessage() override; + + /** + * Returns if mReceivedMessages is not empty. + */ + bool probablyHasInput() const override; + + void assertFinishMessage(uint32_t seq, bool handled); + + void assertNoSentMessages() const; + +private: + // InputMessages received by the endpoint. + std::queue<InputMessage> mReceivedMessages; + // InputMessages sent by the endpoint. + std::queue<InputMessage> mSentMessages; +}; +} // namespace android diff --git a/libs/input/tests/TestLooper.cpp b/libs/input/tests/TestLooper.cpp new file mode 100644 index 0000000000..e0f01ed25d --- /dev/null +++ b/libs/input/tests/TestLooper.cpp @@ -0,0 +1,51 @@ +/** + * 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. + */ + +#include <TestLooper.h> + +#include <android-base/logging.h> + +namespace android { + +TestLooper::TestLooper() : mLooper(sp<Looper>::make(/*allowNonCallbacks=*/false)) {} + +int TestLooper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, + void* data) { + mCallbacks[fd] = callback; + constexpr int SUCCESS{1}; + return SUCCESS; +} + +int TestLooper::removeFd(int fd) { + if (auto it = mCallbacks.find(fd); it != mCallbacks.cend()) { + mCallbacks.erase(fd); + constexpr int SUCCESS{1}; + return SUCCESS; + } + constexpr int FAILURE{0}; + return FAILURE; +} + +void TestLooper::invokeCallback(int fd, int events) { + auto it = mCallbacks.find(fd); + LOG_IF(FATAL, it == mCallbacks.cend()) << "Fd does not exist in mCallbacks."; + mCallbacks[fd]->handleEvent(fd, events, /*data=*/nullptr); +} + +sp<Looper> TestLooper::getLooper() const { + return mLooper; +} +} // namespace android
\ No newline at end of file diff --git a/libs/input/tests/TestLooper.h b/libs/input/tests/TestLooper.h new file mode 100644 index 0000000000..3242bc7699 --- /dev/null +++ b/libs/input/tests/TestLooper.h @@ -0,0 +1,56 @@ +/** + * 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 <map> + +#include <input/LooperInterface.h> + +namespace android { +/** + * TestLooper provides a mechanism to directly trigger Looper's callback. + */ +class TestLooper final : public LooperInterface { +public: + TestLooper(); + + /** + * Adds a file descriptor to mCallbacks. Ident, events, and data parameters are ignored. If + * addFd is called with an existent file descriptor and a different callback, the previous + * callback is overwritten. + */ + int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, + void* data) override; + + /** + * Removes a file descriptor from mCallbacks. If fd is not in mCallbacks, returns FAILURE. + */ + int removeFd(int fd) override; + + /** + * Calls handleEvent of the file descriptor. Fd must be in mCallbacks. Otherwise, invokeCallback + * fatally logs. + */ + void invokeCallback(int fd, int events); + + sp<Looper> getLooper() const override; + +private: + std::map<int /*fd*/, sp<LooperCallback>> mCallbacks; + sp<Looper> mLooper; +}; +} // namespace android
\ No newline at end of file |