Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 193becb4 authored by Arman Uguray's avatar Arman Uguray
Browse files

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
parent 6aa7dc21
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -50,7 +50,8 @@ config("linux") {
  ]

  cflags_cc = [
    "-std=c++11"
    "-std=c++11",
    "-fno-exceptions",
  ]

  defines = [
+10 −2
Original line number Diff line number Diff line
@@ -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 += \
+35 −3
Original line number Diff line number Diff line
@@ -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",
  ]
}
+56 −31
Original line number Diff line number Diff line
@@ -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() {
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;
}
+93 −0
Original line number Diff line number Diff line
//
//  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
Loading