aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArman Uguray <armansito@google.com>2015-07-16 18:12:13 -0700
committerArman Uguray <armansito@google.com>2015-07-24 21:16:03 +0000
commit065d0f709343129f82ff3d072dcb36f612038598 (patch)
tree01f4456de6bb2d1c539e6fdb013a82139e31201e
parentf2d6434a6b7ad98e20c7a601d2a0950237514837 (diff)
downloadbt-065d0f709343129f82ff3d072dcb36f612038598.tar.gz
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
-rw-r--r--build/BUILD.gn3
-rw-r--r--service/Android.mk12
-rw-r--r--service/BUILD.gn38
-rw-r--r--service/main.cpp87
-rw-r--r--service/settings.cpp93
-rw-r--r--service/settings.h69
-rw-r--r--service/switches.h37
-rw-r--r--service/test/settings_unittest.cpp74
-rw-r--r--service/test/uuid_unittest.cpp (renamed from service/uuid_test.cpp)2
9 files changed, 377 insertions, 38 deletions
diff --git a/build/BUILD.gn b/build/BUILD.gn
index 9147f580d..fb20a34f6 100644
--- a/build/BUILD.gn
+++ b/build/BUILD.gn
@@ -50,7 +50,8 @@ config("linux") {
]
cflags_cc = [
- "-std=c++11"
+ "-std=c++11",
+ "-fno-exceptions",
]
defines = [
diff --git a/service/Android.mk b/service/Android.mk
index 64856d146..2d57e913c 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -17,10 +17,17 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := uuid_test.cpp uuid.cpp
+LOCAL_SRC_FILES := \
+ settings.cpp \
+ test/settings_unittest.cpp \
+ test/uuid_unittest.cpp \
+ uuid.cpp
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/../
LOCAL_CFLAGS += -std=c++11
LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := uuid_test_bd
+LOCAL_MODULE := bt_service_unittests
+LOCAL_SHARED_LIBRARIES += libchrome-host
include $(BUILD_HOST_NATIVE_TEST)
include $(CLEAR_VARS)
@@ -32,6 +39,7 @@ LOCAL_SRC_FILES := \
host.cpp \
logging_helpers.cpp \
main.cpp \
+ settings.cpp \
uuid.cpp
LOCAL_C_INCLUDES += \
diff --git a/service/BUILD.gn b/service/BUILD.gn
index 4beeaedee..b21243874 100644
--- a/service/BUILD.gn
+++ b/service/BUILD.gn
@@ -14,14 +14,14 @@
# limitations under the License.
#
-executable("bluetoothtbd") {
+source_set("service") {
sources = [
"a2dp_source.cpp",
"core_stack.cpp",
"gatt_server.cpp",
"host.cpp",
"logging_helpers.cpp",
- "main.cpp",
+ "settings.cpp",
"uuid.cpp"
]
@@ -29,12 +29,44 @@ executable("bluetoothtbd") {
"//",
"//third_party/libchrome"
]
+}
+
+executable("bluetoothtbd") {
+ sources = [
+ "main.cpp"
+ ]
deps = [
+ ":service",
"//btcore",
"//third_party/libchrome:base",
- "//third_party/modp_b64",
+ "//third_party/modp_b64"
+ ]
+
+ include_dirs = [
+ "//",
+ "//third_party/libchrome"
]
libs = [ "-ldl", "-lpthread", "-lrt" ]
}
+
+executable("service_unittests") {
+ testonly = true
+ sources = [
+ "test/settings_unittest.cpp",
+ "test/uuid_unittest.cpp",
+ ]
+
+ include_dirs = [
+ "//",
+ "//third_party/libchrome"
+ ]
+
+ deps = [
+ ":service",
+ "//third_party/gtest:gtest_main",
+ "//third_party/libchrome:base",
+ "//third_party/modp_b64",
+ ]
+}
diff --git a/service/main.cpp b/service/main.cpp
index 46bff4aef..da973f301 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 @@ int main() {
// 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;
}
diff --git a/service/settings.cpp b/service/settings.cpp
new file mode 100644
index 000000000..874fc4e52
--- /dev/null
+++ b/service/settings.cpp
@@ -0,0 +1,93 @@
+//
+// Copyright (C) 2015 Google, Inc.
+//
+// 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 "service/settings.h"
+
+#include <base/command_line.h>
+#include <base/lazy_instance.h>
+#include <base/logging.h>
+
+#include "service/switches.h"
+
+namespace bluetooth {
+
+namespace {
+
+// The global settings instance. We use a LazyInstance here so that we can
+// lazily initialize the instance AND guarantee that it will be cleaned up at
+// exit time without violating the Google C++ style guide.
+base::LazyInstance<Settings> g_settings = LAZY_INSTANCE_INITIALIZER;
+
+void LogRequiredOption(const std::string& option) {
+ LOG(ERROR) << "Required option: \"" << option << "\"";
+}
+
+} // namespace
+
+// static
+bool Settings::Initialize() {
+ return g_settings.Get().Init();
+}
+
+// static
+const Settings& Settings::Get() {
+ CHECK(g_settings.Get().initialized_);
+ return g_settings.Get();
+}
+
+Settings::Settings() : initialized_(false) {
+}
+
+Settings::~Settings() {
+}
+
+bool Settings::Init() {
+ CHECK(!initialized_);
+ auto command_line = base::CommandLine::ForCurrentProcess();
+
+ // Since we have only one meaningful command-line flag for now, it's OK to
+ // hard-code this here. As we add more switches, we should process this in a
+ // more meaningful way.
+ if (command_line->GetSwitches().size() > 1) {
+ LOG(ERROR) << "Unexpected command-line switches found";
+ return false;
+ }
+
+ if (!command_line->HasSwitch(switches::kIPCSocketPath)) {
+ LogRequiredOption(switches::kIPCSocketPath);
+ return false;
+ }
+
+ base::FilePath path = command_line->GetSwitchValuePath(
+ switches::kIPCSocketPath);
+ if (path.value().empty() || path.EndsWithSeparator()) {
+ LOG(ERROR) << "Invalid IPC socket path";
+ return false;
+ }
+
+ // The daemon has no arguments
+ if (command_line->GetArgs().size()) {
+ LOG(ERROR) << "Unexpected command-line arguments found";
+ return false;
+ }
+
+ ipc_socket_path_ = path;
+
+ initialized_ = true;
+ return true;
+}
+
+} // namespace bluetooth
diff --git a/service/settings.h b/service/settings.h
new file mode 100644
index 000000000..f600465ae
--- /dev/null
+++ b/service/settings.h
@@ -0,0 +1,69 @@
+//
+// Copyright (C) 2015 Google, Inc.
+//
+// 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 <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+
+namespace bluetooth {
+
+// The Settings class stores global runtime configurations, such as IPC domain
+// namespace, configuration file locations, and other system properties and
+// flags.
+class Settings {
+ public:
+ // Constant for the "--help" command-line switches.
+ static const char kHelp[];
+
+ // Initializes Settings from the command-line arguments and switches for the
+ // current process. Returns false if initialization fails, for example if an
+ // incorrect command-line option has been given.
+ static bool Initialize();
+
+ // Non-mutable getter for the global Settings object. Use this getter for
+ // accessing settings in a read-only fashion (which should be the case for
+ // most of the code that wants to use this class).
+ static const Settings& Get();
+
+ // DO NOT call these directly. Instead, interact with the global instance
+ // using the static Initialize and Get methods.
+ Settings();
+ ~Settings();
+
+ // TODO(armansito): Write an instance method for storing things into a file.
+
+ // Path to the unix domain socket for Bluetooth IPC. On Android, this needs to
+ // match the init provided socket domain prefix. Outside Android, this will be
+ // the path for the traditional Unix domain socket that the daemon will
+ // create.
+ const base::FilePath& ipc_socket_path() const {
+ return ipc_socket_path_;
+ }
+
+ private:
+ // Instance helper for the static Initialize() method.
+ bool Init();
+
+ bool initialized_;
+ base::FilePath ipc_socket_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(Settings);
+};
+
+} // namespace bluetooth
diff --git a/service/switches.h b/service/switches.h
new file mode 100644
index 000000000..4dd9f1b18
--- /dev/null
+++ b/service/switches.h
@@ -0,0 +1,37 @@
+//
+// Copyright (C) 2015 Google, Inc.
+//
+// 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 <string>
+
+namespace bluetooth {
+namespace switches {
+
+// List of command-line switches used by the daemon.
+const char kHelpLong[] = "help";
+const char kHelpShort[] = "h";
+const char kIPCSocketPath[] = "ipc-socket";
+
+const char kHelpMessage[] =
+ "\nBluetooth System Service\n"
+ "\n"
+ "Usage:\n"
+ "\t--help,-h\tShow this help message\n"
+ "\t--ipc-socket\tSocket path used for IPC";
+
+} // namespace switches
+} // namespace bluetooth
diff --git a/service/test/settings_unittest.cpp b/service/test/settings_unittest.cpp
new file mode 100644
index 000000000..4c8a05625
--- /dev/null
+++ b/service/test/settings_unittest.cpp
@@ -0,0 +1,74 @@
+//
+// Copyright (C) 2015 Google, Inc.
+//
+// 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 <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/macros.h>
+#include <gtest/gtest.h>
+
+#include "service/settings.h"
+#include "service/switches.h"
+
+using bluetooth::Settings;
+using namespace bluetooth::switches;
+
+namespace {
+
+class SettingsTest : public ::testing::Test {
+ public:
+ SettingsTest() = default;
+
+ void TearDown() override {
+ base::CommandLine::Reset();
+ }
+
+ private:
+ base::AtExitManager exit_manager;
+
+ DISALLOW_COPY_AND_ASSIGN(SettingsTest);
+};
+
+TEST_F(SettingsTest, EmptyCommandLine) {
+ const base::CommandLine::CharType* argv[] = { "program" };
+ base::CommandLine::Init(arraysize(argv), argv);
+ EXPECT_FALSE(Settings::Initialize());
+}
+
+TEST_F(SettingsTest, UnexpectedSwitches) {
+ const base::CommandLine::CharType* argv[] = {
+ "program", "--ipc-socket-path=foobar", "--foobarbaz"
+ };
+ base::CommandLine::Init(arraysize(argv), argv);
+ EXPECT_FALSE(Settings::Initialize());
+}
+
+TEST_F(SettingsTest, UnexpectedArguments) {
+ const base::CommandLine::CharType* argv[] = {
+ "program", "--ipc-socket-path=foobar", "foobarbaz"
+ };
+ base::CommandLine::Init(arraysize(argv), argv);
+ EXPECT_FALSE(Settings::Initialize());
+}
+
+TEST_F(SettingsTest, GoodArguments) {
+ const base::CommandLine::CharType* argv[] = {
+ "program", "--ipc-socket=foobar"
+ };
+ base::CommandLine::Init(arraysize(argv), argv);
+ EXPECT_TRUE(Settings::Initialize());
+}
+
+} // namespace
diff --git a/service/uuid_test.cpp b/service/test/uuid_unittest.cpp
index a04c33637..6e69a4817 100644
--- a/service/uuid_test.cpp
+++ b/service/test/uuid_unittest.cpp
@@ -19,7 +19,7 @@
#include <gtest/gtest.h>
-#include "uuid.h"
+#include "service/uuid.h"
using namespace bluetooth;