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