// Abstract class for dispatching tasks.
class EventLoop {
public:
+ enum ReadyMode {
+ kModeInput,
+ kModeOutput
+ };
+
virtual ~EventLoop() {}
// Enqueues a callback.
// This function can be called on any thread.
virtual void PostDelayedTask(const std::function<void()>& callback,
int64_t delay_ms) = 0;
- //TODO(nywang): monitoring file descriptor for data
+
+ // Monitoring file descriptor for data.
+ // Callback will be executed when specific file descriptor is ready.
+ // File descriptor is provided as a parameter to this callback:
+ // This function can be called on any thread.
+ // This returns true upon success and returns false when it failed.
+ virtual bool WatchFileDescriptor(
+ int fd,
+ ReadyMode mode,
+ const std::function<void(int)>& callback_) = 0;
};
} // namespace wificond
#include <looper_backed_event_loop.h>
+#include <android-base/logging.h>
#include <utils/Looper.h>
#include <utils/Timers.h>
DISALLOW_COPY_AND_ASSIGN(EventLoopCallback);
};
+class WatchFdCallback : public android::LooperCallback {
+ public:
+ explicit WatchFdCallback(const std::function<void(int)>& callback)
+ : callback_(callback) {
+ }
+
+ ~WatchFdCallback() override = default;
+
+ virtual int handleEvent(int fd, int events, void* data) {
+ callback_(fd);
+ return 0;
+ }
+
+ private:
+ const std::function<void(int)> callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatchFdCallback);
+};
+
} // namespace
namespace android {
looper_->sendMessageDelayed(ms2ns(delay_ms), looper_callback, NULL);
}
+bool LooperBackedEventLoop::WatchFileDescriptor(
+ int fd,
+ ReadyMode mode,
+ const std::function<void(int)>& callback) {
+ sp<android::LooperCallback> watch_fd_callback = new WatchFdCallback(callback);
+ int event;
+ if (mode == kModeInput) {
+ event = Looper::EVENT_INPUT;
+ } else if (mode == kModeOutput) {
+ event = Looper::EVENT_OUTPUT;
+ } else {
+ LOG(ERROR) << "Invalid mode for WatchFileDescriptor().";
+ return false;
+ }
+ // addFd() returns 1 if descriptor was added, 0 if arguments were invalid.
+ // Since we are using non-NULL callback, the second parameter 'ident' will
+ // always be ignored. It is OK to use 0 for 'ident'.
+ // See Looper.h for more details.
+ if (looper_->addFd(fd, 0, event, watch_fd_callback, NULL) == 0) {
+ LOG(ERROR) << "Invalid arguments for Looper::addFd().";
+ return false;
+ }
+ return true;
+}
+
void LooperBackedEventLoop::Poll() {
while (should_continue_) {
looper_->pollOnce(-1);
// See event_loop.h
void PostDelayedTask(const std::function<void()>& callback,
int64_t delay_ms) override;
+ // See event_loop.h
+ bool WatchFileDescriptor(
+ int fd,
+ ReadyMode mode,
+ const std::function<void(int)>& callback) override;
// Performs all pending callbacks and waiting for new events until
// TriggerExit() is called.
} // namespace
-void callback_for_test() {
- LOG(INFO) << "callback is executed...";
-}
-
int main(int argc, char** argv) {
android::base::InitLogging(argv);
LOG(INFO) << "wificond is starting up...";
std::unique_ptr<android::wificond::LooperBackedEventLoop> event_dispatcher_(
new android::wificond::LooperBackedEventLoop());
ScopedSignalHandler scoped_signal_handler(event_dispatcher_.get());
- //TODO(nywang): b//28982981, Remove this when we have unittest for event loop checked in.
- event_dispatcher_->PostTask(&callback_for_test);
- event_dispatcher_->PostDelayedTask(&callback_for_test, 5000);
event_dispatcher_->Poll();
LOG(INFO) << "Leaving the loop...";
return 0;
* limitations under the License.
*/
+#include <string.h>
+
#include <memory>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
+#include <utils/Errors.h>
#include <utils/StopWatch.h>
#include <looper_backed_event_loop.h>
const int kTimingToleranceMs = 25;
+// Adapt from libutils/tests/TestHelpers.h
+class Pipe {
+public:
+ android::base::unique_fd send_fd;
+ android::base::unique_fd receive_fd;
+
+ Pipe() {
+ int fds[2];
+ ::pipe(fds);
+
+ receive_fd = android::base::unique_fd(fds[0]);
+ send_fd = android::base::unique_fd(fds[1]);
+ }
+
+ bool writeSignal() {
+ ssize_t n_written = ::write(send_fd, "*", 1);
+ if (n_written != 1) {
+ LOG(ERROR) << "Failed to write signal to pipe: " << strerror(errno);
+ return false;
+ }
+ return true;
+ }
+
+ bool readSignal() {
+ char buf[1];
+ ssize_t n_read = ::read(receive_fd, buf, 1);
+ if (n_read != 1) {
+ if (n_read == 0) {
+ LOG(ERROR) << "No data from pipe";
+ } else {
+ LOG(ERROR) << "Failed to read signal from pipe: " << strerror(errno);
+ }
+ return false;
+ }
+ return true;
+ }
+};
+
} // namespace
namespace android {
EXPECT_TRUE(task_executed);
}
+TEST_F(WificondLooperBackedEventLoopTest, LooperBackedEventLoopWatchFdInputReadyTest) {
+ Pipe pipe;
+ android::status_t read_result;
+ android::status_t write_result;
+ event_loop_->PostTask([&write_result, &pipe]() {write_result = pipe.writeSignal();});
+ // Read data from pipe when fd is ready for input.
+ EXPECT_TRUE(event_loop_->WatchFileDescriptor(
+ pipe.receive_fd,
+ EventLoop::kModeInput,
+ [&read_result, &pipe, this](int fd) {
+ read_result = pipe.readSignal();
+ event_loop_->TriggerExit();}));
+ event_loop_->Poll();
+ EXPECT_EQ(true, read_result);
+ EXPECT_EQ(true, write_result);
+}
+
+TEST_F(WificondLooperBackedEventLoopTest, LooperBackedEventLoopWatchFdOutputReadyTest) {
+ Pipe pipe;
+ android::status_t write_result;
+ // Write data to pipe when fd is ready for output.
+ EXPECT_TRUE(event_loop_->WatchFileDescriptor(
+ pipe.send_fd,
+ EventLoop::kModeOutput,
+ [&write_result, &pipe, this](int fd) {
+ write_result = pipe.writeSignal();
+ event_loop_->TriggerExit();}));
+ event_loop_->Poll();
+ EXPECT_EQ(true, write_result);
+ EXPECT_EQ(true, pipe.readSignal());
+}
+
} // namespace wificond
} // namespace android