// // Copyright (C) 2020 The Android Open Source Project // // 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 "host/commands/modem_simulator/misc_service.h" #include #include #include "host/commands/modem_simulator/device_config.h" namespace cuttlefish { MiscService::MiscService(int32_t service_id, ChannelMonitor* channel_monitor, ThreadLooper* thread_looper) : ModemService(service_id, this->InitializeCommandHandlers(), channel_monitor, thread_looper) { ParseTimeZone(); } void MiscService::ParseTimeZone() { #if defined(__linux__) constexpr char TIMEZONE_FILENAME[] = "/etc/timezone"; std::ifstream ifs = modem::DeviceConfig::open_ifstream_crossplat(TIMEZONE_FILENAME); if (ifs.is_open()) { std::string line; if (std::getline(ifs, line)) { FixTimeZone(line); timezone_ = line; } } #endif } void MiscService::FixTimeZone(std::string& line) { auto slashpos = line.find("/"); // "/" will be treated as separator, change it ! if (slashpos != std::string::npos) { line.replace(slashpos, 1, "!"); } } void MiscService::SetTimeZone(std::string timezone) { FixTimeZone(timezone); timezone_ = timezone; } std::vector MiscService::InitializeCommandHandlers() { std::vector command_handlers = { /* initializeCallback */ CommandHandler("E0Q0V1", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("S0=0", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CMEE=1", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CMOD=0", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CSSN=0,1", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+COLP=0", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CSCS=\"HEX\"", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CMGF=0", [this](const Client& client) { this->HandleCommandDefaultSupported(client); }), CommandHandler("+CGSN", [this](const Client& client, std::string& cmd) { this->HandleGetIMEI(client, cmd); }), CommandHandler("+REMOTETIMEUPDATE", [this](const Client& client, std::string& cmd) { this->HandleTimeUpdate(client, cmd); }), }; return (command_handlers); } void MiscService::HandleGetIMEI(const Client& client, std::string& command) { const std::string identityGsmImei = "867400022047199"; const std::string identityGsmSvn = "01"; const std::string information = "modem simulator"; std::vector responses; if (command == "AT+CGSN") { responses.push_back(identityGsmImei); } else { CommandParser cmd(command); cmd.SkipPrefix(); int snt = cmd.GetNextInt(); switch (snt) { case 0: // SN: IMEI and more information provided by manufacturers responses.push_back(identityGsmImei + information); break; case 1: // IMEI responses.push_back(identityGsmImei); break; case 2: // IMEI and software version number responses.push_back(identityGsmImei + identityGsmSvn); break; case 3: // Software version number responses.push_back(identityGsmSvn); break; default: // Default IMEI responses.push_back(identityGsmImei); break; } } responses.push_back("OK"); client.SendCommandResponse(responses); } void MiscService::HandleTimeUpdate(const Client& client, std::string& command) { (void)client; (void)command; TimeUpdate(); } long MiscService::TimeZoneOffset(time_t* utctime) { struct tm local = *std::localtime(utctime); time_t local_time = std::mktime(&local); struct tm gmt = *std::gmtime(utctime); // mktime() converts struct tm according to local timezone. time_t gmt_time = std::mktime(&gmt); return (long)difftime(local_time, gmt_time); } void MiscService::TimeUpdate() { auto now = std::time(0); auto local_time = *std::localtime(&now); auto gm_time = *std::gmtime(&now); // Timezone offset is in number of quarter-hours auto tzdiff = TimeZoneOffset(&now) / (15 * 60); std::stringstream ss; ss << "%CTZV: " << std::setfill('0') << std::setw(2) << gm_time.tm_year % 100 << "/" << std::setfill('0') << std::setw(2) << gm_time.tm_mon + 1 << "/" << std::setfill('0') << std::setw(2) << gm_time.tm_mday << ":" << std::setfill('0') << std::setw(2) << gm_time.tm_hour << ":" << std::setfill('0') << std::setw(2) << gm_time.tm_min << ":" << std::setfill('0') << std::setw(2) << gm_time.tm_sec << (tzdiff >= 0 ? '+' : '-') << (tzdiff >= 0 ? tzdiff : -tzdiff) << ":" << local_time.tm_isdst; if (!timezone_.empty()) { ss << ":" << timezone_; } SendUnsolicitedCommand(ss.str()); } } // namespace cuttlefish