service: Introduce a global Settings object

Introduced a global Settings object that will store all runtime properties that
would be associated with a config file, Android system properties, etc. Added a
mechanism to parse command-line options which can be used to pass paths to
configuration files, file path for UNIX domain socket based IPC mechanism, and
any other property that is dynamic in nature. This will help us remove hardcoded
paths, strings, and other such values in the future.

Bug: 22532366
Change-Id: I8e790363ed31d44369f7991a8ea7132d1cace70b
diff --git a/service/main.cpp b/service/main.cpp
index 46bff4a..da973f3 100644
--- a/service/main.cpp
+++ b/service/main.cpp
@@ -21,6 +21,10 @@
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/files/scoped_file.h>
+
 #define LOG_TAG "bt_host"
 // For system properties
 // TODO(icoolidge): abstraction or non-cutils stub.
@@ -28,44 +32,63 @@
 #include <cutils/properties.h>
 #endif  // !defined(OS_GENERIC)
 
-#include "core_stack.h"
-#include "host.h"
 #include "osi/include/log.h"
 #include "osi/include/socket_utils/sockets.h"
+#include "service/core_stack.h"
+#include "service/host.h"
+#include "service/settings.h"
+#include "service/switches.h"
 
 namespace {
 
 // TODO(armansito): None of these should be hardcoded here. Instead, pass these
 // via commandline.
 const char kDisableProperty[] = "persist.bluetooth.disable";
-const char kSocketFromInit[] = "bluetooth";
-const char kUnixIpcSocketPath[] = "bluetooth-ipc-socket";
 
 }  // namespace
 
-int main() {
-  // TODO(armansito): Move all  of the IPC connection establishment into its own
+int main(int argc, char *argv[]) {
+  base::AtExitManager exit_manager;
+  base::CommandLine::Init(argc, argv);
+
+  // TODO(armansito): Initialize base/logging. By default it will dump to stdout
+  // but we might want to change that based on a command-line switch. Figure out
+  // how to route the logging to Android's syslog. Once that's done, we won't
+  // need to use osi/include/log.h anymore.
+
+  // TODO(armansito): Register exit-time clean-up handlers for the IPC sockets.
+  // Register signal handlers.
+  auto command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(bluetooth::switches::kHelpLong) ||
+      command_line->HasSwitch(bluetooth::switches::kHelpShort)) {
+    LOG(INFO) << bluetooth::switches::kHelpMessage;
+    return EXIT_SUCCESS;
+  }
+
+  if (!bluetooth::Settings::Initialize()) {
+    LOG(ERROR) << "Failed to parse the command-line.";
+    return EXIT_FAILURE;
+  }
+
+  // TODO(armansito): Move all of the IPC connection establishment into its own
   // class. Here we should only need to initialize and start the main
   // MessageLoop and the CoreStack instance.
   int status;
 
 #if !defined(OS_GENERIC)
+  // TODO(armansito): Remove Chromecast specific property out of here. This
+  // should just be obtained from global config.
   char disable_value[PROPERTY_VALUE_MAX];
   status = property_get(kDisableProperty, disable_value, nullptr);
   if (status && !strcmp(disable_value, "1")) {
-    LOG_INFO(LOG_TAG, "%s", "service disabled");
+    LOG(INFO) << "service disabled";
     return EXIT_SUCCESS;
   }
+#endif  // !defined(OS_GENERIC)
 
-  int server_socket = osi_android_get_control_socket(kSocketFromInit);
-  if (server_socket < 0) {
-    LOG_ERROR(LOG_TAG, "failed to get socket from init");
-    return EXIT_FAILURE;
-  }
-#else  // defined(OS_GENERIC)
-  int server_socket = socket(PF_UNIX, SOCK_SEQPACKET, 0);
-  if (server_socket < 0) {
-    LOG_ERROR(LOG_TAG, "failed to open domain socket for IPC");
+  base::ScopedFD server_socket(socket(PF_UNIX, SOCK_SEQPACKET, 0));
+  if (!server_socket.is_valid()) {
+    LOG(ERROR) << "failed to open domain socket for IPC";
     return EXIT_FAILURE;
   }
 
@@ -75,43 +98,45 @@
   // this properly.
   //
   // Also, the daemon should clean this up properly as it shuts down.
-  unlink(kUnixIpcSocketPath);
+  unlink(bluetooth::Settings::Get().ipc_socket_path().value().c_str());
 
   struct sockaddr_un address;
   memset(&address, 0, sizeof(address));
   address.sun_family = AF_UNIX;
-  strncpy(address.sun_path, kUnixIpcSocketPath, sizeof(address.sun_path) - 1);
-
-  if (bind(server_socket, (struct sockaddr*)&address, sizeof(address)) < 0) {
-    LOG_ERROR(LOG_TAG, "Failed to bind IPC socket to address");
+  strncpy(address.sun_path,
+          bluetooth::Settings::Get().ipc_socket_path().value().c_str(),
+          sizeof(address.sun_path) - 1);
+  if (bind(server_socket.get(), (struct sockaddr*)&address,
+           sizeof(address)) < 0) {
+    LOG(ERROR) << "Failed to bind IPC socket to address: " << strerror(errno);
     return EXIT_FAILURE;
   }
 
-#endif  // !defined(OS_GENERIC)
-
-  status = listen(server_socket, SOMAXCONN);
+  status = listen(server_socket.get(), SOMAXCONN);
   if (status < 0) {
-    LOG_ERROR(LOG_TAG, "listen failed: %s", strerror(errno));
+    LOG(ERROR) << "Failed to listen on IPC socket: " << strerror(errno);
     return EXIT_FAILURE;
   }
 
   bluetooth::CoreStack bt;
-  bt.Initialize();
+  if (!bt.Initialize()) {
+    LOG(ERROR) << "Failed to initialize the Bluetooth stack";
+    return EXIT_FAILURE;
+  }
 
   // TODO(icoolidge): accept simultaneous clients
   while (true) {
-    int client_socket = accept4(server_socket, nullptr, nullptr, SOCK_NONBLOCK);
+    int client_socket = accept4(server_socket.get(), nullptr,
+                                nullptr, SOCK_NONBLOCK);
     if (status == -1) {
-      LOG_ERROR(LOG_TAG, "accept failed: %s", strerror(errno));
+      LOG(ERROR) << "accept failed: %s" << strerror(errno);
       return EXIT_FAILURE;
     }
 
-    LOG_INFO(LOG_TAG, "client connected: %d", client_socket);
+    LOG(INFO) << "client connected: %d" << client_socket;
     bluetooth::Host bluetooth_host(client_socket, &bt);
     bluetooth_host.EventLoop();
   }
 
-  close(server_socket);
-
   return EXIT_SUCCESS;
 }