blob: 005d9f9d87d9601e9158bf7c041055edc59f5571 [file] [log] [blame]
//
// 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 <fstream>
#include <iomanip>
#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<CommandHandler> MiscService::InitializeCommandHandlers() {
std::vector<CommandHandler> 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<std::string> 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