1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 #include "host/commands/modem_simulator/misc_service.h"
17
18 #include <fstream>
19 #include <iomanip>
20
21 #include "host/commands/modem_simulator/device_config.h"
22
23 namespace cuttlefish {
24
MiscService(int32_t service_id,ChannelMonitor * channel_monitor,ThreadLooper * thread_looper)25 MiscService::MiscService(int32_t service_id, ChannelMonitor* channel_monitor,
26 ThreadLooper* thread_looper)
27 : ModemService(service_id, this->InitializeCommandHandlers(),
28 channel_monitor, thread_looper) {
29 ParseTimeZone();
30 }
31
ParseTimeZone()32 void MiscService::ParseTimeZone() {
33 #if defined(__linux__)
34 constexpr char TIMEZONE_FILENAME[] = "/etc/timezone";
35 std::ifstream ifs = modem::DeviceConfig::open_ifstream_crossplat(TIMEZONE_FILENAME);
36 if (ifs.is_open()) {
37 std::string line;
38 if (std::getline(ifs, line)) {
39 FixTimeZone(line);
40 timezone_ = line;
41 }
42 }
43 #endif
44 }
45
FixTimeZone(std::string & line)46 void MiscService::FixTimeZone(std::string& line) {
47 auto slashpos = line.find("/");
48 // "/" will be treated as separator, change it !
49 if (slashpos != std::string::npos) {
50 line.replace(slashpos, 1, "!");
51 }
52 }
53
SetTimeZone(std::string timezone)54 void MiscService::SetTimeZone(std::string timezone) {
55 FixTimeZone(timezone);
56 timezone_ = timezone;
57 }
58
InitializeCommandHandlers()59 std::vector<CommandHandler> MiscService::InitializeCommandHandlers() {
60 std::vector<CommandHandler> command_handlers = {
61 /* initializeCallback */
62 CommandHandler("E0Q0V1",
63 [this](const Client& client) {
64 this->HandleCommandDefaultSupported(client);
65 }),
66 CommandHandler("S0=0",
67 [this](const Client& client) {
68 this->HandleCommandDefaultSupported(client);
69 }),
70 CommandHandler("+CMEE=1",
71 [this](const Client& client) {
72 this->HandleCommandDefaultSupported(client);
73 }),
74 CommandHandler("+CMOD=0",
75 [this](const Client& client) {
76 this->HandleCommandDefaultSupported(client);
77 }),
78 CommandHandler("+CSSN=0,1",
79 [this](const Client& client) {
80 this->HandleCommandDefaultSupported(client);
81 }),
82 CommandHandler("+COLP=0",
83 [this](const Client& client) {
84 this->HandleCommandDefaultSupported(client);
85 }),
86 CommandHandler("+CSCS=\"HEX\"",
87 [this](const Client& client) {
88 this->HandleCommandDefaultSupported(client);
89 }),
90 CommandHandler("+CMGF=0",
91 [this](const Client& client) {
92 this->HandleCommandDefaultSupported(client);
93 }),
94
95 CommandHandler("+CGSN",
96 [this](const Client& client, std::string& cmd) {
97 this->HandleGetIMEI(client, cmd);
98 }),
99 CommandHandler("+REMOTETIMEUPDATE",
100 [this](const Client& client, std::string& cmd) {
101 this->HandleTimeUpdate(client, cmd);
102 }),
103 };
104 return (command_handlers);
105 }
106
HandleGetIMEI(const Client & client,std::string & command)107 void MiscService::HandleGetIMEI(const Client& client, std::string& command) {
108 const std::string identityGsmImei = "867400022047199";
109 const std::string identityGsmSvn = "01";
110 const std::string information = "modem simulator";
111
112 std::vector<std::string> responses;
113
114 if (command == "AT+CGSN") {
115 responses.push_back(identityGsmImei);
116 } else {
117 CommandParser cmd(command);
118 cmd.SkipPrefix();
119 int snt = cmd.GetNextInt();
120 switch (snt) {
121 case 0: // SN: IMEI and more information provided by manufacturers
122 responses.push_back(identityGsmImei + information);
123 break;
124 case 1: // IMEI
125 responses.push_back(identityGsmImei);
126 break;
127 case 2: // IMEI and software version number
128 responses.push_back(identityGsmImei + identityGsmSvn);
129 break;
130 case 3: // Software version number
131 responses.push_back(identityGsmSvn);
132 break;
133 default: // Default IMEI
134 responses.push_back(identityGsmImei);
135 break;
136 }
137 }
138
139 responses.push_back("OK");
140 client.SendCommandResponse(responses);
141 }
142
HandleTimeUpdate(const Client & client,std::string & command)143 void MiscService::HandleTimeUpdate(const Client& client, std::string& command) {
144 (void)client;
145 (void)command;
146 TimeUpdate();
147 }
148
TimeZoneOffset(time_t * utctime)149 long MiscService::TimeZoneOffset(time_t* utctime)
150 {
151 struct tm local = *std::localtime(utctime);
152 time_t local_time = std::mktime(&local);
153 struct tm gmt = *std::gmtime(utctime);
154 // mktime() converts struct tm according to local timezone.
155 time_t gmt_time = std::mktime(&gmt);
156 return (long)difftime(local_time, gmt_time);
157 }
158
TimeUpdate()159 void MiscService::TimeUpdate() {
160 auto now = std::time(0);
161
162 auto local_time = *std::localtime(&now);
163 auto gm_time = *std::gmtime(&now);
164
165 // Timezone offset is in number of quarter-hours
166 auto tzdiff = TimeZoneOffset(&now) / (15 * 60);
167
168 std::stringstream ss;
169 ss << "%CTZV: " << std::setfill('0') << std::setw(2)
170 << gm_time.tm_year % 100 << "/" << std::setfill('0') << std::setw(2)
171 << gm_time.tm_mon + 1 << "/" << std::setfill('0') << std::setw(2)
172 << gm_time.tm_mday << ":" << std::setfill('0') << std::setw(2)
173 << gm_time.tm_hour << ":" << std::setfill('0') << std::setw(2)
174 << gm_time.tm_min << ":" << std::setfill('0') << std::setw(2)
175 << gm_time.tm_sec << (tzdiff >= 0 ? '+' : '-')
176 << (tzdiff >= 0 ? tzdiff : -tzdiff) << ":" << local_time.tm_isdst;
177 if (!timezone_.empty()) {
178 ss << ":" << timezone_;
179 }
180
181 SendUnsolicitedCommand(ss.str());
182 }
183
184 } // namespace cuttlefish
185