From fb4f424325d09b8b759f5d245d9494c567df33e1 Mon Sep 17 00:00:00 2001 From: Simon Fels Date: Tue, 28 Jun 2016 00:19:36 +0200 Subject: [PATCH] First bits for an GSM emulator implementation Not clear yet if we will use this or not. --- .../network/qemu_pipe_connection_creator.cpp | 7 +- .../network/qemu_pipe_connection_creator.h | 1 + src/anbox/support/at_parser.cpp | 74 ++++++++++++ src/anbox/support/at_parser.h | 48 ++++++++ src/anbox/support/gsm_message_processor.cpp | 111 ++++++++++++++++++ src/anbox/support/gsm_message_processor.h | 59 ++++++++++ src/anbox/support/telephony_manager.cpp | 38 ++++++ src/anbox/support/telephony_manager.h | 40 +++++++ tests/CMakeLists.txt | 29 +++++ tests/anbox/CMakeLists.txt | 1 + tests/anbox/support/CMakeLists.txt | 1 + tests/anbox/support/at_parser_tests.cpp | 47 ++++++++ 12 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 src/anbox/support/at_parser.cpp create mode 100644 src/anbox/support/at_parser.h create mode 100644 src/anbox/support/gsm_message_processor.cpp create mode 100644 src/anbox/support/gsm_message_processor.h create mode 100644 src/anbox/support/telephony_manager.cpp create mode 100644 src/anbox/support/telephony_manager.h create mode 100644 tests/anbox/CMakeLists.txt create mode 100644 tests/anbox/support/CMakeLists.txt create mode 100644 tests/anbox/support/at_parser_tests.cpp diff --git a/src/anbox/network/qemu_pipe_connection_creator.cpp b/src/anbox/network/qemu_pipe_connection_creator.cpp index d6b95f7..33cd86e 100644 --- a/src/anbox/network/qemu_pipe_connection_creator.cpp +++ b/src/anbox/network/qemu_pipe_connection_creator.cpp @@ -27,6 +27,7 @@ #include "anbox/support/sensors_message_processor.h" #include "anbox/support/camera_message_processor.h" #include "anbox/support/fingerprint_message_processor.h" +#include "anbox/support/gsm_message_processor.h" namespace ba = boost::asio; @@ -56,8 +57,6 @@ void QemuPipeConnectionCreator::create_connection_for( messenger, messenger, next_id(), connections_, processor); connections_->add(connection); connection->read_next_message(); - - DEBUG("id %i", connection->id()); } QemuPipeConnectionCreator::client_type QemuPipeConnectionCreator::identify_client( @@ -94,6 +93,8 @@ QemuPipeConnectionCreator::client_type QemuPipeConnectionCreator::identify_clien return client_type::qemud_camera; else if (utils::string_starts_with(identifier_and_args, "pipe:qemud:fingerprintlisten")) return client_type::qemud_fingerprint; + else if (utils::string_starts_with(identifier_and_args, "pipe:qemud:gsm")) + return client_type::qemud_gsm; return client_type::invalid; } @@ -111,6 +112,8 @@ std::shared_ptr QemuPipeConnectionCreator::create_processor(co return std::make_shared(messenger); else if (type == client_type::qemud_fingerprint) return std::make_shared(messenger); + else if (type == client_type::qemud_gsm) + return std::make_shared(messenger); return std::make_shared(); } diff --git a/src/anbox/network/qemu_pipe_connection_creator.h b/src/anbox/network/qemu_pipe_connection_creator.h index a132982..c9d5b41 100644 --- a/src/anbox/network/qemu_pipe_connection_creator.h +++ b/src/anbox/network/qemu_pipe_connection_creator.h @@ -50,6 +50,7 @@ public: qemud_sensors, qemud_camera, qemud_fingerprint, + qemud_gsm, }; private: diff --git a/src/anbox/support/at_parser.cpp b/src/anbox/support/at_parser.cpp new file mode 100644 index 0000000..759e2d4 --- /dev/null +++ b/src/anbox/support/at_parser.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/support/at_parser.h" +#include "anbox/utils.h" +#include "anbox/logger.h" + +namespace anbox { +namespace support { +AtParser::AtParser() { +} + +void AtParser::register_command(const std::string &command, CommandHandler handler) { + handlers_.insert({ command, handler }); +} + +void AtParser::process_data(std::vector &data) { + size_t bytes_processed = 0; + for (size_t pos = 0; pos < data.size(); ) { + const auto byte = data.at(pos); + if (byte == '\n' || byte == '\r') { + std::string command; + command.insert(0, reinterpret_cast(data.data()) + bytes_processed, pos - bytes_processed); + bytes_processed += (pos - bytes_processed) + 1; + processs_command(command); + } + pos++; + } + + data.erase(data.begin(), data.begin() + bytes_processed); +} + +void AtParser::processs_command(const std::string &command) { + if (!utils::string_starts_with(command, "AT")) { + WARNING("Invalid AT command: '%s'", command); + return; + } + + // Strip AT prefix from command + auto real_command = command.substr(2, command.length() - 2); + + DEBUG("command: %s", real_command); + + CommandHandler handler = nullptr; + for (const auto &iter : handlers_) { + if (utils::string_starts_with(real_command, iter.first)) { + handler = iter.second; + break; + } + } + + if (!handler) { + WARNING("No handler for command '%s' available", real_command); + return; + } + + handler(real_command); +} +} // namespace support +} // namespace anbox diff --git a/src/anbox/support/at_parser.h b/src/anbox/support/at_parser.h new file mode 100644 index 0000000..13f95b2 --- /dev/null +++ b/src/anbox/support/at_parser.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_SUPPORT_AT_PARSER_H_ +#define ANBOX_SUPPORT_AT_PARSER_H_ + +#include +#include +#include +#include + +#include "anbox/do_not_copy_or_move.h" + +namespace anbox { +namespace support { +class AtParser { +public: + typedef std::function CommandHandler; + + AtParser(); + + void register_command(const std::string &command, CommandHandler handler); + + void process_data(std::vector &data); + +private: + void processs_command(const std::string &command); + + std::map handlers_; +}; +} // namespace support +} // namespace anbox + +#endif diff --git a/src/anbox/support/gsm_message_processor.cpp b/src/anbox/support/gsm_message_processor.cpp new file mode 100644 index 0000000..0b137c7 --- /dev/null +++ b/src/anbox/support/gsm_message_processor.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/logger.h" +#include "anbox/support/gsm_message_processor.h" +#include "anbox/support/at_parser.h" + +#include +#include + +using namespace std::placeholders; + +namespace anbox { +namespace support { +GsmMessageProcessor::GsmMessageProcessor(const std::shared_ptr &messenger) : + messenger_(messenger), + parser_(std::make_shared()) { + + auto ok_reply = [&](const std::string&) { send_reply("OK"); }; + + parser_->register_command("E0Q0V1", ok_reply); + parser_->register_command("S0=0", ok_reply); + parser_->register_command("+CTEC", std::bind(&GsmMessageProcessor::handle_ctec, this, _1)); + parser_->register_command("+CMEE=1", ok_reply); + parser_->register_command("+CCWA=1", ok_reply); + parser_->register_command("+CMOD=0", ok_reply); + parser_->register_command("+CMUT=0", ok_reply); + parser_->register_command("+CSSN=0,1", ok_reply); + parser_->register_command("+COLP=0", ok_reply); + parser_->register_command("+CSCS=\"HEX\"", ok_reply); + parser_->register_command("+CUSD=1", ok_reply); + parser_->register_command("+CGEREP=1,0", ok_reply); + parser_->register_command("+CMGF", std::bind(&GsmMessageProcessor::handle_cmgf, this, _1)); + parser_->register_command("%CPI=3", ok_reply); + parser_->register_command("%CSTAT=1", ok_reply); + parser_->register_command("+CREG", std::bind(&GsmMessageProcessor::handle_creg, this, _1)); + parser_->register_command("+CGREG", std::bind(&GsmMessageProcessor::handle_cgreg, this, _1)); + parser_->register_command("+CFUN", std::bind(&GsmMessageProcessor::handle_cfun, this, _1)); +} + +GsmMessageProcessor::~GsmMessageProcessor() { +} + +bool GsmMessageProcessor::process_data(const std::vector &data) { + for (const auto &byte : data) + buffer_.push_back(byte); + + parser_->process_data(buffer_); + return true; +} + +void GsmMessageProcessor::send_reply(const std::string &message) { + auto reply = utils::string_format("%s\rOK\n", message); + std::vector data; + std::copy(reply.begin(), reply.end(), std::back_inserter(data)); + messenger_->send(reinterpret_cast(data.data()), data.size()); +} + +void GsmMessageProcessor::handle_ctec(const std::string &command) { + if (command == "+CTEC=?") + send_reply("+CTEC: 0,1,2,3"); + else if (command == "+CTEC?") + send_reply(utils::string_format("+CTEC: %d,%x", static_cast(technology::gsm), 0x0f)); +} + +void GsmMessageProcessor::handle_cmgf(const std::string &command) { + DEBUG("command %s", command); + if (command == "+CMGF=0") + send_reply(""); +} + +void GsmMessageProcessor::handle_creg(const std::string &command) { + if (command == "+CREG=?") + send_reply("+CREG: (0-2)"); + else if (command == "+CREG?") + send_reply(utils::string_format("+CREF: %d,%d", 0, 0)); + else if (utils::string_starts_with(command, "+CREG=")) + send_reply(""); +} + +void GsmMessageProcessor::handle_cgreg(const std::string &command) { + if (command == "+CGREG=?") + send_reply("+CGREG: (0-2)"); + else if (command == "+CGREG?") + send_reply(utils::string_format("+CGREG: %d,%d", 0, 0)); + else if (utils::string_starts_with(command, "+CGREG=")) + send_reply(""); +} + +void GsmMessageProcessor::handle_cfun(const std::string &command) { + if (command == "+CFUN?") + send_reply(utils::string_format("+CFUN: %d", 1)); + else if (utils::string_starts_with(command, "+CFUN=")) + send_reply(""); +} +} // namespace support +} // namespace anbox diff --git a/src/anbox/support/gsm_message_processor.h b/src/anbox/support/gsm_message_processor.h new file mode 100644 index 0000000..be818ab --- /dev/null +++ b/src/anbox/support/gsm_message_processor.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_SUPPORT_GSM_MESSAGE_PROCESSOR_H_ +#define ANBOX_SUPPORT_GSM_MESSAGE_PROCESSOR_H_ + +#include "anbox/network/message_processor.h" +#include "anbox/network/socket_messenger.h" + +namespace anbox { +namespace support { +class AtParser; +class GsmMessageProcessor : public network::MessageProcessor { +public: + GsmMessageProcessor(const std::shared_ptr &messenger); + ~GsmMessageProcessor(); + + bool process_data(const std::vector &data) override; + +private: + enum class technology { + gsm = 0, + wcdm, + cdma, + evdo, + lte, + unknown, + }; + + void send_reply(const std::string &message); + + void handle_ctec(const std::string &command); + void handle_cmgf(const std::string &command); + void handle_creg(const std::string &command); + void handle_cgreg(const std::string &command); + void handle_cfun(const std::string &command); + + std::shared_ptr messenger_; + std::vector buffer_; + std::shared_ptr parser_; +}; +} // namespace graphics +} // namespace anbox + +#endif diff --git a/src/anbox/support/telephony_manager.cpp b/src/anbox/support/telephony_manager.cpp new file mode 100644 index 0000000..e612ad9 --- /dev/null +++ b/src/anbox/support/telephony_manager.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/support/telephony_manager.h" +#include "anbox/dbus/ofono.h" +#include "anbox/logger.h" + +namespace anbox { +namespace support { +TelephonyManager::TelephonyManager(const core::dbus::Bus::Ptr &bus) : + bus_(bus) { + ofono_ = core::dbus::Service::use_service(bus_, "org.ofono"); + modem_ = ofono_->object_for_path({"/ril_0"}); + + auto netreg_prop_changed = modem_->get_signal(); + netreg_prop_changed->connect([&](const org::ofono::NetworkRegistration::Signals::PropertyChanged::ArgumentType &arguments) { + DEBUG("org::ofono::NetworkRegistration::PropertyChanged"); + }); +} + +TelephonyManager::~TelephonyManager() { +} +} // namespace support +} // namespace anbox diff --git a/src/anbox/support/telephony_manager.h b/src/anbox/support/telephony_manager.h new file mode 100644 index 0000000..23600cd --- /dev/null +++ b/src/anbox/support/telephony_manager.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#ifndef ANBOX_SUPPORT_TELEPHONY_MANAGER_H_ +#define ANBOX_SUPPORT_TELEPHONY_MANAGER_H_ + +#include +#include +#include + +namespace anbox { +namespace support { +class TelephonyManager { +public: + TelephonyManager(const core::dbus::Bus::Ptr &bus); + ~TelephonyManager(); + +private: + core::dbus::Bus::Ptr bus_; + core::dbus::Service::Ptr ofono_; + core::dbus::Object::Ptr modem_; +}; +} // namespace support +} // namespace anbox + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e69de29..1ada917 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -0,0 +1,29 @@ +include_directories( + ${Boost_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/src +) + +macro(ANBOX_ADD_TEST test_name src) + add_executable( + ${test_name} + ${src} + ) + + target_link_libraries( + ${test_name} + + anbox-core + + gmock + gmock_main + + ${ARGN} + + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ) + + add_test(${test_name} ${CMAKE_CURRENT_BINARY_DIR}/${test_name} --gtest_filter=*-*requires*) +endmacro(ANBOX_ADD_TEST) + +add_subdirectory(anbox) diff --git a/tests/anbox/CMakeLists.txt b/tests/anbox/CMakeLists.txt new file mode 100644 index 0000000..2772486 --- /dev/null +++ b/tests/anbox/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(support) diff --git a/tests/anbox/support/CMakeLists.txt b/tests/anbox/support/CMakeLists.txt new file mode 100644 index 0000000..386e44e --- /dev/null +++ b/tests/anbox/support/CMakeLists.txt @@ -0,0 +1 @@ +ANBOX_ADD_TEST(at_parser_tests at_parser_tests.cpp) diff --git a/tests/anbox/support/at_parser_tests.cpp b/tests/anbox/support/at_parser_tests.cpp new file mode 100644 index 0000000..0432632 --- /dev/null +++ b/tests/anbox/support/at_parser_tests.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 Simon Fels + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + * + */ + +#include "anbox/support/at_parser.h" + +#include + +#include + +TEST(AtParser, BasicCommands) { + anbox::support::AtParser parser; + + std::string command = "ATE0Q0V1\nATE0Q0V1\n"; + std::vector data; + std::copy(command.begin(), command.end(), std::back_inserter(data)); + + int commands_expected = 0; + int commands_found = 0; + auto assert_at_command = [&](const std::string &expected_command) { + commands_expected++; + return [&](const std::string &command) { + commands_found++; + ASSERT_STRCASEEQ(expected_command.c_str(), command.c_str()); + }; + }; + + parser.register_command("E0Q0V1", assert_at_command("E0Q0V1")); + parser.register_command("E0Q0V1", assert_at_command("E0Q0V1")); + + parser.process_data(data); + + ASSERT_EQ(commands_expected, commands_found); +}