add MainLoop am: 3b1d20a572 am: 519e17b2a8
am: 468798aa84

Change-Id: I740f97e2dff87aa7680a1c82b1cc28c4b01a3ec6
diff --git a/Android.mk b/Android.mk
index a25d048..b06568d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -33,6 +33,7 @@
 LOCAL_C_INCLUDES := $(wifilogd_includes)
 LOCAL_SRC_FILES := \
     command_processor.cpp \
+    main_loop.cpp \
     memory_reader.cpp \
     message_buffer.cpp \
     os.cpp \
@@ -55,6 +56,7 @@
     tests/command_processor_unittest.cpp \
     tests/local_utils_unittest.cpp \
     tests/main.cpp \
+    tests/main_loop_unittest.cpp \
     tests/memory_reader_unittest.cpp \
     tests/message_buffer_unittest.cpp \
     tests/mock_command_processor.cpp \
diff --git a/main_loop.cpp b/main_loop.cpp
new file mode 100644
index 0000000..62bed31
--- /dev/null
+++ b/main_loop.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 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 <array>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <utility>
+
+#include "android-base/logging.h"
+
+#include "wifilogd/main_loop.h"
+#include "wifilogd/protocol.h"
+
+namespace android {
+namespace wifilogd {
+
+namespace {
+constexpr auto kMainBufferSizeBytes = 128 * 1024;
+}
+
+MainLoop::MainLoop(const std::string& socket_name)
+    : MainLoop(socket_name, std::make_unique<Os>(),
+               std::make_unique<CommandProcessor>(kMainBufferSizeBytes)) {}
+
+MainLoop::MainLoop(const std::string& socket_name, std::unique_ptr<Os> os,
+                   std::unique_ptr<CommandProcessor> command_processor)
+    : os_(std::move(os)), command_processor_(std::move(command_processor)) {
+  Os::Errno err;
+  std::tie(sock_fd_, err) = os_->GetControlSocket(socket_name);
+  if (err) {
+    LOG(FATAL) << "Failed to get control socket: " << std::strerror(errno);
+  }
+}
+
+void MainLoop::RunOnce() {
+  std::array<uint8_t, protocol::kMaxMessageSize> input_buf;
+  size_t datagram_len;
+  Os::Errno err;
+  std::tie(datagram_len, err) =
+      os_->ReceiveDatagram(sock_fd_, input_buf.data(), input_buf.size());
+  if (err) {
+    // TODO(b/32098735): Increment stats counter.
+    // TODO(b/32481888): Improve error handling.
+    return;
+  }
+
+  if (datagram_len > protocol::kMaxMessageSize) {
+    // TODO(b/32098735): Increment stats counter.
+    datagram_len = protocol::kMaxMessageSize;
+  }
+
+  command_processor_->ProcessCommand(input_buf.data(), datagram_len,
+                                     Os::kInvalidFd);
+}
+
+}  // namespace wifilogd
+}  // namespace android
diff --git a/main_loop.h b/main_loop.h
new file mode 100644
index 0000000..c5a4b48
--- /dev/null
+++ b/main_loop.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef MAIN_LOOP_H_
+#define MAIN_LOOP_H_
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+
+#include "wifilogd/command_processor.h"
+#include "wifilogd/os.h"
+
+namespace android {
+namespace wifilogd {
+
+// The main event loop for wifilogd.
+class MainLoop {
+ public:
+  explicit MainLoop(const std::string& socket_name);
+  MainLoop(const std::string& socket_name, std::unique_ptr<Os> os,
+           std::unique_ptr<CommandProcessor> command_processor);
+
+  // Runs one iteration of the loop.
+  void RunOnce();
+
+ private:
+  std::unique_ptr<Os> os_;
+  std::unique_ptr<CommandProcessor> command_processor_;
+  // We use an int, rather than a unique_fd, because the file
+  // descriptor's lifetime is managed by init. (init creates
+  // the socket before forking our process.)
+  int sock_fd_;
+
+  DISALLOW_COPY_AND_ASSIGN(MainLoop);
+};
+
+}  // namespace wifilogd
+}  // namespace android
+
+#endif  // MAIN_LOOP_H_
diff --git a/tests/main_loop_unittest.cpp b/tests/main_loop_unittest.cpp
new file mode 100644
index 0000000..db04027
--- /dev/null
+++ b/tests/main_loop_unittest.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016, 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 <cerrno>
+#include <memory>
+#include <tuple>
+#include <utility>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "wifilogd/tests/mock_command_processor.h"
+#include "wifilogd/tests/mock_os.h"
+
+#include "wifilogd/main_loop.h"
+#include "wifilogd/protocol.h"
+
+namespace android {
+namespace wifilogd {
+namespace {
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Ge;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+constexpr int kControlSocketFd = 100;
+constexpr char kFakeSocketName[] = "fake-socket";
+
+class MainLoopTest : public ::testing::Test {
+ public:
+  MainLoopTest()
+      : os_(new StrictMock<MockOs>()),
+        command_processor_(new StrictMock<MockCommandProcessor>()) {
+    EXPECT_CALL(*os_, GetControlSocket(kFakeSocketName))
+        .WillOnce(Return(std::tuple<size_t, Os::Errno>{kControlSocketFd, 0}));
+    main_loop_ = std::make_unique<MainLoop>(
+        kFakeSocketName, std::unique_ptr<Os>{os_},
+        std::unique_ptr<CommandProcessor>{command_processor_});
+  }
+
+ protected:
+  std::unique_ptr<MainLoop> main_loop_;
+  // We use raw pointers to access the mocks, since ownership passes
+  // to |main_loop_|.
+  StrictMock<MockOs>* os_;
+  StrictMock<MockCommandProcessor>* command_processor_;
+};
+
+}  // namespace
+
+TEST_F(MainLoopTest, RunOnceReadsFromCorrectSocket) {
+  EXPECT_CALL(*os_, ReceiveDatagram(kControlSocketFd, _, _));
+  EXPECT_CALL(*command_processor_, ProcessCommand(_, _, _)).Times(AnyNumber());
+  main_loop_->RunOnce();
+}
+
+TEST_F(MainLoopTest, RunOnceReadsWithSufficientlyLargeBuffer) {
+  EXPECT_CALL(*os_, ReceiveDatagram(_, _, Ge(protocol::kMaxMessageSize)));
+  EXPECT_CALL(*command_processor_, ProcessCommand(_, _, _)).Times(AnyNumber());
+  main_loop_->RunOnce();
+}
+
+TEST_F(MainLoopTest, RunOncePassesSmallestValidMessageToCommandProcessor) {
+  EXPECT_CALL(*os_, ReceiveDatagram(_, _, _))
+      .WillOnce(
+          Return(std::tuple<size_t, Os::Errno>{sizeof(protocol::Command), 0}));
+  EXPECT_CALL(*command_processor_,
+              ProcessCommand(_, sizeof(protocol::Command), _));
+  main_loop_->RunOnce();
+}
+
+TEST_F(MainLoopTest, RunOncePassesLargestValidMessageToCommandProcessor) {
+  EXPECT_CALL(*os_, ReceiveDatagram(_, _, _))
+      .WillOnce(
+          Return(std::tuple<size_t, Os::Errno>{protocol::kMaxMessageSize, 0}));
+  EXPECT_CALL(*command_processor_,
+              ProcessCommand(_, protocol::kMaxMessageSize, _));
+  main_loop_->RunOnce();
+}
+
+TEST_F(MainLoopTest, RunOncePassesRuntMessageToCommandProcessor) {
+  EXPECT_CALL(*os_, ReceiveDatagram(_, _, _))
+      .WillOnce(Return(std::tuple<size_t, Os::Errno>{0, 0}));
+  EXPECT_CALL(*command_processor_, ProcessCommand(_, 0, _));
+  main_loop_->RunOnce();
+}
+
+TEST_F(MainLoopTest, RunOnceLimitsMaxSizeReportedToCommandProcessor) {
+  EXPECT_CALL(*os_, ReceiveDatagram(_, _, _))
+      .WillOnce(Return(
+          std::tuple<size_t, Os::Errno>{protocol::kMaxMessageSize + 1, 0}));
+  EXPECT_CALL(*command_processor_,
+              ProcessCommand(_, protocol::kMaxMessageSize, _));
+  main_loop_->RunOnce();
+}
+
+TEST_F(MainLoopTest, RunOnceDoesNotPassDataToCommandProcessorOnError) {
+  EXPECT_CALL(*os_, ReceiveDatagram(_, _, protocol::kMaxMessageSize))
+      .WillOnce(Return(std::tuple<size_t, Os::Errno>{0, EINTR}));
+  EXPECT_CALL(*command_processor_, ProcessCommand(_, _, _)).Times(0);
+  main_loop_->RunOnce();
+}
+
+// Per
+// github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-tests,
+// death tests should be specially named.
+using MainLoopDeathTest = MainLoopTest;
+
+TEST_F(MainLoopDeathTest, CtorFailureToFetchControlSocketCausesDeath) {
+  auto os = std::make_unique<StrictMock<MockOs>>();
+  auto command_processor = std::make_unique<StrictMock<MockCommandProcessor>>();
+  ON_CALL(*os, GetControlSocket(kFakeSocketName))
+      .WillByDefault(Return(std::tuple<size_t, Os::Errno>{-1, ERANGE}));
+  EXPECT_DEATH(
+      MainLoop(kFakeSocketName, std::move(os), std::move(command_processor)),
+      "Failed to get control socket");
+}
+
+}  // namespace wifilogd
+}  // namespace android