LOCAL_SRC_FILES := \
a2dp_source.cpp \
core_stack.cpp \
+ daemon.cpp \
gatt_server.cpp \
- host.cpp \
+ ipc/ipc_handler.cpp \
+ ipc/ipc_handler_unix.cpp \
+ ipc/ipc_manager.cpp \
+ ipc/unix_ipc_host.cpp \
logging_helpers.cpp \
main.cpp \
settings.cpp \
sources = [
"a2dp_source.cpp",
"core_stack.cpp",
+ "daemon.cpp",
"gatt_server.cpp",
- "host.cpp",
+ "ipc/ipc_handler.cpp",
+ "ipc/ipc_handler_unix.cpp",
+ "ipc/ipc_manager.cpp",
+ "ipc/unix_ipc_host.cpp",
"logging_helpers.cpp",
"settings.cpp",
"uuid.cpp"
--- /dev/null
+//
+// 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/daemon.h"
+
+#include <base/logging.h>
+
+#include "service/core_stack.h"
+#include "service/ipc/ipc_manager.h"
+#include "service/settings.h"
+
+namespace bluetooth {
+
+namespace {
+
+// The global Daemon instance.
+Daemon* g_daemon = nullptr;
+
+} // namespace
+
+// static
+bool Daemon::Initialize() {
+ CHECK(!g_daemon);
+
+ g_daemon = new Daemon();
+ if (g_daemon->Init())
+ return true;
+
+ LOG(ERROR) << "Failed to initialize the Daemon object";
+
+ delete g_daemon;
+ g_daemon = nullptr;
+
+ return false;
+}
+
+// static
+void Daemon::ShutDown() {
+ CHECK(g_daemon);
+ CHECK(g_daemon->initialized_);
+
+ delete g_daemon;
+ g_daemon = NULL;
+}
+
+// static
+Daemon* Daemon::Get() {
+ CHECK(g_daemon);
+ return g_daemon;
+}
+
+Daemon::Daemon() : initialized_(false) {
+}
+
+Daemon::~Daemon() {
+}
+
+void Daemon::StartMainLoop() {
+ CHECK(initialized_);
+ message_loop_->Run();
+}
+
+bool Daemon::Init() {
+ CHECK(!initialized_);
+
+ message_loop_.reset(new base::MessageLoop());
+
+ settings_.reset(new Settings());
+ if (!settings_->Init()) {
+ LOG(ERROR) << "Failed to set up Settings";
+ return false;
+ }
+
+ core_stack_.reset(new CoreStack());
+ if (!core_stack_->Initialize()) {
+ LOG(ERROR) << "Failed to set up CoreStack";
+ return false;
+ }
+
+ ipc_manager_.reset(new ipc::IPCManager(core_stack_.get()));
+
+ // If an IPC socket path was given, initialize the UNIX domain socket based
+ // IPC layer.
+ if (!settings_->ipc_socket_path().empty() &&
+ !ipc_manager_->Start(ipc::IPCManager::TYPE_UNIX)) {
+ LOG(ERROR) << "Failed to set up UNIX domain-socket IPCManager";
+ return false;
+ }
+
+ initialized_ = true;
+
+ return true;
+}
+
+} // namespace bluetooth
--- /dev/null
+//
+// 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 <memory>
+
+#include <base/macros.h>
+#include <base/message_loop/message_loop.h>
+
+namespace ipc {
+class IPCManager;
+} // namespace ipc
+
+namespace bluetooth {
+
+class CoreStack;
+class Settings;
+
+// The Daemon class is a singleton that represents the root of the ownership
+// hierarchy. The single instance sets up and owns the main event loop, the IPC
+// handlers, global Settings, and the core Bluetooth stack.
+class Daemon {
+ public:
+ // Initializes the daemon. This must be called to at the start of the
+ // application to set up the global daemon instance and everything it manages.
+ // Returns false in case of a failure.
+ static bool Initialize();
+
+ // Cleans up all the resources associated with the global Daemon object.
+ static void ShutDown();
+
+ // Returns the singleton Daemon instance. All classes can interact with the
+ // Daemon, obtain its resources etc using this getter.
+ static Daemon* Get();
+
+ // The global Settings object. All classes have direct access to this through
+ // the Daemon object.
+ Settings* settings() const { return settings_.get(); }
+
+ // The main event loop. This should be used for any events and delayed tasks
+ // that should be executed on the daemon's main thread.
+ base::MessageLoop* message_loop() const { return message_loop_.get(); }
+
+ // Starts the daemon's main loop.
+ void StartMainLoop();
+
+ private:
+ Daemon();
+ ~Daemon();
+
+ // Private instance helper for Initialize().
+ bool Init();
+
+ bool initialized_;
+ std::unique_ptr<base::MessageLoop> message_loop_;
+ std::unique_ptr<Settings> settings_;
+ std::unique_ptr<CoreStack> core_stack_;
+ std::unique_ptr<ipc::IPCManager> ipc_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(Daemon);
+};
+
+} // namespace bluetooth
--- /dev/null
+//
+// 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/ipc/ipc_handler.h"
+
+#include <base/logging.h>
+
+namespace ipc {
+
+IPCHandler::IPCHandler(bluetooth::CoreStack* core_stack)
+ : core_stack_(core_stack) {
+ CHECK(core_stack_);
+}
+
+IPCHandler::~IPCHandler() {
+}
+
+} // namespace ipc
--- /dev/null
+//
+// 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 <base/macros.h>
+#include <base/memory/ref_counted.h>
+
+namespace bluetooth {
+class CoreStack;
+} // namespace bluetooth
+
+namespace ipc {
+
+// IPCHandler is an interface that classes implementing different IPC mechanisms
+// must conform to.
+class IPCHandler : public base::RefCountedThreadSafe<IPCHandler> {
+ public:
+ explicit IPCHandler(bluetooth::CoreStack* core_stack);
+ virtual ~IPCHandler();
+
+ // Initializes and runs the IPC mechanis. Returns true on success, false
+ // otherwise.
+ virtual bool Run() = 0;
+
+ protected:
+ // Weak reference to the global CoreStack instance.
+ bluetooth::CoreStack* core_stack_;
+
+ private:
+ IPCHandler() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(IPCHandler);
+};
+
+} // namespace ipc
--- /dev/null
+//
+// 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/ipc/ipc_handler_unix.h"
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <base/bind.h>
+
+#include "service/daemon.h"
+#include "service/ipc/unix_ipc_host.h"
+#include "service/settings.h"
+
+namespace ipc {
+
+IPCHandlerUnix::IPCHandlerUnix(bluetooth::CoreStack* core_stack)
+ : IPCHandler(core_stack),
+ running_(false),
+ thread_("IPCHandlerUnix") {
+}
+
+IPCHandlerUnix::~IPCHandlerUnix() {
+}
+
+bool IPCHandlerUnix::Run() {
+ CHECK(!running_);
+
+ const base::FilePath& path =
+ bluetooth::Daemon::Get()->settings()->ipc_socket_path();
+ if (path.empty()) {
+ LOG(ERROR) << "No domain socket path provided";
+ return false;
+ }
+
+ CHECK(base::MessageLoop::current()); // An origin event loop is required.
+ origin_task_runner_ = base::MessageLoop::current()->task_runner();
+
+ // TODO(armansito): This is opens the door to potentially unlinking files in
+ // the current directory that we're not supposed to. For now we will have an
+ // assumption that the daemon runs in a sandbox but we should generally do
+ // this properly.
+ //
+ // Also, the daemon should clean this up properly as it shuts down.
+ unlink(path.value().c_str());
+
+ 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 false;
+ }
+
+ struct sockaddr_un address;
+ memset(&address, 0, sizeof(address));
+ address.sun_family = AF_UNIX;
+ strncpy(address.sun_path, 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 false;
+ }
+
+ socket_.swap(server_socket);
+ running_ = true; // Set this here before launching the thread.
+
+ // Start an IO thread and post the listening task.
+ base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
+ if (!thread_.StartWithOptions(options)) {
+ LOG(ERROR) << "Failed to start IPCHandlerUnix thread";
+ running_ = false;
+ return false;
+ }
+
+ thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&IPCHandlerUnix::StartListeningOnThread, this));
+
+ return true;
+}
+
+void IPCHandlerUnix::StartListeningOnThread() {
+ CHECK(socket_.is_valid());
+ CHECK(core_stack_);
+ CHECK(running_);
+
+ LOG(INFO) << "Listening to incoming connections";
+
+ int status = listen(socket_.get(), SOMAXCONN);
+ if (status < 0) {
+ LOG(ERROR) << "Failed to listen on domain socket: " << strerror(errno);
+ origin_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&IPCHandlerUnix::ShutDownOnOriginThread, this));
+ return;
+ }
+
+ // TODO(icoolidge): accept simultaneous clients
+ while (true) {
+ int client_socket = accept4(socket_.get(), nullptr, nullptr, SOCK_NONBLOCK);
+ if (status == -1) {
+ LOG(ERROR) << "Failed to accept client connection: " << strerror(errno);
+ continue;
+ }
+
+ LOG(INFO) << "Established client connection: fd=" << client_socket;
+ UnixIPCHost ipc_host(client_socket, core_stack_);
+ // TODO(armansito): Use |thread_|'s MessageLoopForIO instead of using a
+ // custom event loop to poll from the socket.
+ ipc_host.EventLoop();
+ }
+}
+
+void IPCHandlerUnix::ShutDownOnOriginThread() {
+ LOG(INFO) << "Shutting down IPCHandlerUnix thread";
+ thread_.Stop();
+ running_ = false;
+
+ // TODO(armansito): Notify the upper layer so that they can perform clean-up
+ // tasks on unexpected shut-down.
+}
+
+} // namespace ipc
--- /dev/null
+//
+// 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 <base/files/scoped_file.h>
+#include <base/macros.h>
+#include <base/threading/thread.h>
+
+#include "service/ipc/ipc_handler.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace bluetooth {
+class CoreStack;
+} // namespace bluetooth
+
+namespace ipc {
+
+// Implements a UNIX domain-socket based IPCHandler
+class IPCHandlerUnix : public IPCHandler {
+ public:
+ explicit IPCHandlerUnix(bluetooth::CoreStack* core_stack);
+ ~IPCHandlerUnix() override;
+
+ // IPCHandler override:
+ bool Run() override;
+
+ private:
+ IPCHandlerUnix() = default;
+
+ // Starts listening for incoming connections. Posted on |thread_| by Run().
+ void StartListeningOnThread();
+
+ // Stops the IPC thread. This helper is needed since base::Thread requires
+ // threads to be stopped on the thread that started them.
+ void ShutDownOnOriginThread();
+
+ // True, if the IPC mechanism is running.
+ bool running_;
+
+ // The server socket on which we listen to incoming connections.
+ base::ScopedFD socket_;
+
+ // We use a dedicated thread for listening to incoming connections and
+ // polling from the socket to avoid blocking the main thread.
+ base::Thread thread_;
+
+ // The origin thread's task runner.
+ scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(IPCHandlerUnix);
+};
+
+} // namespace ipc
--- /dev/null
+//
+// 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/ipc/ipc_manager.h"
+
+#include "service/ipc/ipc_handler_unix.h"
+
+namespace ipc {
+
+IPCManager::IPCManager(bluetooth::CoreStack* core_stack)
+ : core_stack_(core_stack) {
+ CHECK(core_stack_);
+}
+
+IPCManager::~IPCManager() {
+}
+
+bool IPCManager::Start(Type type) {
+ switch (type) {
+ case TYPE_UNIX:
+ unix_handler_ = new IPCHandlerUnix(core_stack_);
+ return unix_handler_->Run();
+ case TYPE_BINDER:
+ // TODO(armansito): Support Binder
+ default:
+ LOG(ERROR) << "Unsupported IPC type given: " << type;
+ }
+
+ return false;
+}
+
+bool IPCManager::BinderStarted() const {
+ return binder_handler_.get();
+}
+
+bool IPCManager::UnixStarted() const {
+ return unix_handler_.get();
+}
+
+} // namespace ipc
--- /dev/null
+//
+// 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 <memory>
+
+#include <base/macros.h>
+#include <base/memory/ref_counted.h>
+
+namespace bluetooth {
+class CoreStack;
+} // namespace bluetooth
+
+namespace ipc {
+
+class IPCHandler;
+
+// IPCManager is a class for initializing and running supported IPC mechanisms.
+// It manages the life-time of different IPC flavors that are available on the
+// system. There are two flavors: a plain UNIX domain socket based system and
+// one based on the Binder-based android.bluetooth framework.
+class IPCManager {
+ public:
+ // Possible IPC types.
+ enum Type {
+ TYPE_UNIX, // IPC based on a UNIX domain socket
+ TYPE_BINDER // IPC based on the Binder
+ };
+
+ explicit IPCManager(bluetooth::CoreStack* core_stack);
+ ~IPCManager();
+
+ // Initialize the underlying IPC handler based on |type|, if that type has not
+ // yet been initialized and returns true on success. Returns false if that
+ // type has already been initialized or an error occurs.
+ //
+ // If TYPE_UNIX is given, the file path to use for the domain socket will be
+ // obtained from the global Settings object. Hence, the Settings object must
+ // have been initialized before calling this method.
+ bool Start(Type type);
+
+ // Returns true if an IPC type has been initialized.
+ bool BinderStarted() const;
+ bool UnixStarted() const;
+
+ private:
+ IPCManager() = default;
+
+ // Pointers to the different IPC handler classes. These are initialized and
+ // owned by us.
+ scoped_refptr<IPCHandler> binder_handler_;
+ scoped_refptr<IPCHandler> unix_handler_;
+
+ // The global CoreStack instance that represents the current Bluetooth
+ // adapter.
+ bluetooth::CoreStack* core_stack_;
+
+ DISALLOW_COPY_AND_ASSIGN(IPCManager);
+};
+
+} // namespace ipc
// See the License for the specific language governing permissions and
// limitations under the License.
//
-#include "host.h"
+
+#include "service/ipc/unix_ipc_host.h"
#include <errno.h>
#include <stdio.h>
#define LOG_TAG "bt_bluetooth_host"
#include "osi/include/log.h"
-#include "core_stack.h"
-#include "gatt_server.h"
-#include "uuid.h"
+#include "service/core_stack.h"
+#include "service/gatt_server.h"
+#include "service/uuid.h"
+
+using bluetooth::CoreStack;
+using bluetooth::Uuid;
+
+using namespace bluetooth::gatt;
namespace {
const char kStopServiceCommand[] = "stop-service";
const char kWriteCharacteristicCommand[] = "write-characteristic";
-// Useful values for indexing Host::pfds_
+// Useful values for indexing UnixIPCHost::pfds_
// Not super general considering that we should be able to support
-// many GATT FDs owned by one Host.
+// many GATT FDs owned by one UnixIPCHost.
enum {
kFdIpc = 0,
kFdGatt = 1,
} // namespace
-namespace bluetooth {
+namespace ipc {
-Host::Host(int sockfd, CoreStack* bt)
+UnixIPCHost::UnixIPCHost(int sockfd, CoreStack* bt)
: bt_(bt), pfds_(1, {sockfd, POLLIN, 0}) {}
-Host::~Host() {
+UnixIPCHost::~UnixIPCHost() {
close(pfds_[0].fd);
}
-bool Host::EventLoop() {
+bool UnixIPCHost::EventLoop() {
while (true) {
int status =
TEMP_FAILURE_RETRY(ppoll(pfds_.data(), pfds_.size(), nullptr, nullptr));
return true;
}
-bool Host::OnSetAdapterName(const std::string& name) {
+bool UnixIPCHost::OnSetAdapterName(const std::string& name) {
std::string decoded_data;
base::Base64Decode(name, &decoded_data);
return bt_->SetAdapterName(decoded_data);
}
-bool Host::OnCreateService(const std::string& service_uuid) {
- gatt_servers_[service_uuid] = std::unique_ptr<gatt::Server>(new gatt::Server);
+bool UnixIPCHost::OnCreateService(const std::string& service_uuid) {
+ gatt_servers_[service_uuid] = std::unique_ptr<Server>(new Server);
int gattfd;
bool status =
return true;
}
-bool Host::OnDestroyService(const std::string& service_uuid) {
+bool UnixIPCHost::OnDestroyService(const std::string& service_uuid) {
gatt_servers_.erase(service_uuid);
close(pfds_[1].fd);
pfds_.resize(1);
return true;
}
-bool Host::OnAddCharacteristic(const std::string& service_uuid,
+bool UnixIPCHost::OnAddCharacteristic(const std::string& service_uuid,
const std::string& characteristic_uuid,
const std::string& control_uuid,
const std::string& options) {
if (std::find(option_tokens.begin(), option_tokens.end(), "notify") !=
option_tokens.end()) {
- permissions_mask |= gatt::kPermissionRead;
- properties_mask |= gatt::kPropertyRead;
- properties_mask |= gatt::kPropertyNotify;
+ permissions_mask |= kPermissionRead;
+ properties_mask |= kPropertyRead;
+ properties_mask |= kPropertyNotify;
}
if (std::find(option_tokens.begin(), option_tokens.end(), "read") !=
option_tokens.end()) {
- permissions_mask |= gatt::kPermissionRead;
- properties_mask |= gatt::kPropertyRead;
+ permissions_mask |= kPermissionRead;
+ properties_mask |= kPropertyRead;
}
if (std::find(option_tokens.begin(), option_tokens.end(), "write") !=
option_tokens.end()) {
- permissions_mask |= gatt::kPermissionWrite;
- properties_mask |= gatt::kPropertyWrite;
+ permissions_mask |= kPermissionWrite;
+ properties_mask |= kPropertyWrite;
}
if (control_uuid.empty()) {
return true;
}
-bool Host::OnSetCharacteristicValue(const std::string& service_uuid,
+bool UnixIPCHost::OnSetCharacteristicValue(const std::string& service_uuid,
const std::string& characteristic_uuid,
const std::string& value) {
std::string decoded_data;
return true;
}
-bool Host::OnSetAdvertisement(const std::string& service_uuid,
+bool UnixIPCHost::OnSetAdvertisement(const std::string& service_uuid,
const std::string& advertise_uuids,
const std::string& advertise_data,
const std::string& transmit_name) {
return true;
}
-bool Host::OnSetScanResponse(const std::string& service_uuid,
+bool UnixIPCHost::OnSetScanResponse(const std::string& service_uuid,
const std::string& scan_response_uuids,
const std::string& scan_response_data,
const std::string& transmit_name) {
return true;
}
-bool Host::OnStartService(const std::string& service_uuid) {
+bool UnixIPCHost::OnStartService(const std::string& service_uuid) {
return gatt_servers_[service_uuid]->Start();
}
-bool Host::OnStopService(const std::string& service_uuid) {
+bool UnixIPCHost::OnStopService(const std::string& service_uuid) {
return gatt_servers_[service_uuid]->Stop();
}
-bool Host::OnMessage() {
+bool UnixIPCHost::OnMessage() {
std::string ipc_msg;
int size = recv(pfds_[kFdIpc].fd, &ipc_msg[0], 0, MSG_PEEK | MSG_TRUNC);
if (-1 == size) {
return false;
}
-bool Host::OnGattWrite() {
+bool UnixIPCHost::OnGattWrite() {
Uuid::Uuid128Bit id;
int r = read(pfds_[kFdGatt].fd, id.data(), id.size());
if (r != id.size()) {
return true;
}
-} // namespace bluetooth
+} // namespace ipc
#include <string>
#include <unordered_map>
-#include "gatt_server.h"
-#include "uuid.h"
+#include "service/gatt_server.h"
+#include "service/uuid.h"
namespace bluetooth {
-
class CoreStack;
+} // namespace bluetooth
+
+namespace ipc {
// This implements a single threaded event loop which dispatches
// reads from a set of FDs (pfds_) to a set of handlers.
// Reads from the GATT pipe read end will result in a write to
// to the IPC socket, and vise versa.
-class Host {
+class UnixIPCHost {
public:
- // Host owns the passed sockfd.
- Host(int sockfd, CoreStack* bt);
- ~Host();
+ // UnixIPCHost owns the passed sockfd.
+ UnixIPCHost(int sockfd, bluetooth::CoreStack* bt);
+ ~UnixIPCHost();
// Synchronously handle all events on input FDs.
bool EventLoop();
bool OnStopService(const std::string& service_uuid);
// weak reference.
- CoreStack *bt_;
+ bluetooth::CoreStack *bt_;
// File descripters that we will block against.
std::vector<struct pollfd> pfds_;
// Container for multiple GATT servers. Currently only one is supported.
// TODO(icoolidge): support many to one for real.
- std::unordered_map<std::string, std::unique_ptr<gatt::Server>> gatt_servers_;
+ std::unordered_map<std::string, std::unique_ptr<bluetooth::gatt::Server>>
+ gatt_servers_;
};
-} // namespace bluetooth
+} // namespace ipc
// limitations under the License.
//
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#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.
#if !defined(OS_GENERIC)
#include <cutils/properties.h>
#endif // !defined(OS_GENERIC)
-#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/daemon.h"
#include "service/switches.h"
namespace {
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);
+ int status = property_get(kDisableProperty, disable_value, nullptr);
if (status && !strcmp(disable_value, "1")) {
LOG(INFO) << "service disabled";
return EXIT_SUCCESS;
}
#endif // !defined(OS_GENERIC)
- 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;
- }
-
- // TODO(armansito): This is opens the door to potentially unlinking files in
- // the current directory that we're not supposed to. For now we will have an
- // assumption that the daemon runs in a sandbox but we should generally do
- // this properly.
- //
- // Also, the daemon should clean this up properly as it shuts down.
- 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,
- 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;
- }
-
- status = listen(server_socket.get(), SOMAXCONN);
- if (status < 0) {
- LOG(ERROR) << "Failed to listen on IPC socket: " << strerror(errno);
+ if (!bluetooth::Daemon::Initialize()) {
+ LOG(ERROR) << "Failed to initialize Daemon";
return EXIT_FAILURE;
}
- bluetooth::CoreStack bt;
- if (!bt.Initialize()) {
- LOG(ERROR) << "Failed to initialize the Bluetooth stack";
- return EXIT_FAILURE;
- }
+ // Start the main event loop.
+ bluetooth::Daemon::Get()->StartMainLoop();
- // TODO(icoolidge): accept simultaneous clients
- while (true) {
- int client_socket = accept4(server_socket.get(), nullptr,
- nullptr, SOCK_NONBLOCK);
- if (status == -1) {
- LOG(ERROR) << "accept failed: %s" << strerror(errno);
- return EXIT_FAILURE;
- }
-
- LOG(INFO) << "client connected: %d" << client_socket;
- bluetooth::Host bluetooth_host(client_socket, &bt);
- bluetooth_host.EventLoop();
- }
+ // The main message loop has exited; clean up the Daemon.
+ bluetooth::Daemon::Get()->ShutDown();
return EXIT_SUCCESS;
}
#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) {
}
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;
+ const auto& switches = command_line->GetSwitches();
+
+ for (const auto& iter : switches) {
+ if (iter.first == switches::kIPCSocketPath) {
+ // kIPCSocketPath: An optional argument that initializes an IPC socket
+ // path for IPC. If this is not present, the daemon will default to Binder
+ // for the IPC mechanism.
+ base::FilePath path(iter.second);
+ if (path.empty() || path.EndsWithSeparator()) {
+ LOG(ERROR) << "Invalid IPC socket path";
+ return false;
+ }
+
+ ipc_socket_path_ = path;
+ } else {
+ LOG(ERROR) << "Unexpected command-line switches found";
+ return false;
+ }
}
// The daemon has no arguments
return false;
}
- ipc_socket_path_ = path;
-
initialized_ = true;
return true;
}
// 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.
+ // Initializes the Settings object. This reads the command-line options for
+ // the current process (which must have been initialized using
+ // base::CommandLine) and sets up the initial global settings. Returns false
+ // if there is an error, e.g. if the parameters/switches are malformed.
+ bool Init();
+
// 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
}
private:
- // Instance helper for the static Initialize() method.
- bool Init();
-
bool initialized_;
base::FilePath ipc_socket_path_;
"\n"
"Usage:\n"
"\t--help,-h\tShow this help message\n"
- "\t--ipc-socket\tSocket path used for IPC";
+ "\t--ipc-socket\tSocket path used for domain socket based IPC";
} // namespace switches
} // namespace bluetooth
base::CommandLine::Reset();
}
- private:
- base::AtExitManager exit_manager;
+ protected:
+ base::AtExitManager exit_manager_;
+ Settings settings_;
+ private:
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());
+ EXPECT_TRUE(settings_.Init());
}
-TEST_F(SettingsTest, UnexpectedSwitches) {
+TEST_F(SettingsTest, UnexpectedSwitches1) {
const base::CommandLine::CharType* argv[] = {
"program", "--ipc-socket-path=foobar", "--foobarbaz"
};
base::CommandLine::Init(arraysize(argv), argv);
- EXPECT_FALSE(Settings::Initialize());
+ EXPECT_FALSE(settings_.Init());
+}
+
+TEST_F(SettingsTest, UnexpectedSwitches2) {
+ const base::CommandLine::CharType* argv[] = {
+ "program", "--foobarbaz"
+ };
+ base::CommandLine::Init(arraysize(argv), argv);
+ EXPECT_FALSE(settings_.Init());
+}
+
+TEST_F(SettingsTest, UnexpectedArguments1) {
+ const base::CommandLine::CharType* argv[] = {
+ "program", "foobarbaz"
+ };
+ base::CommandLine::Init(arraysize(argv), argv);
+ EXPECT_FALSE(settings_.Init());
}
-TEST_F(SettingsTest, UnexpectedArguments) {
+TEST_F(SettingsTest, UnexpectedArguments2) {
const base::CommandLine::CharType* argv[] = {
"program", "--ipc-socket-path=foobar", "foobarbaz"
};
base::CommandLine::Init(arraysize(argv), argv);
- EXPECT_FALSE(Settings::Initialize());
+ EXPECT_FALSE(settings_.Init());
}
TEST_F(SettingsTest, GoodArguments) {
"program", "--ipc-socket=foobar"
};
base::CommandLine::Init(arraysize(argv), argv);
- EXPECT_TRUE(Settings::Initialize());
+ EXPECT_TRUE(settings_.Init());
}
} // namespace