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 <ctime>
19 #include <fstream>
20 #include <iomanip>
21
22 namespace cuttlefish {
23
MiscService(int32_t service_id,ChannelMonitor * channel_monitor,ThreadLooper * thread_looper)24 MiscService::MiscService(int32_t service_id, ChannelMonitor* channel_monitor,
25 ThreadLooper* thread_looper)
26 : ModemService(service_id, this->InitializeCommandHandlers(),
27 channel_monitor, thread_looper) {
28 ParseTimeZone();
29 }
30
ParseTimeZone()31 void MiscService::ParseTimeZone() {
32 #if defined(__linux__)
33 constexpr char TIMEZONE_FILENAME[] = "/etc/timezone";
34 std::ifstream ifs(TIMEZONE_FILENAME);
35 if (ifs.is_open()) {
36 std::string line;
37 if (std::getline(ifs, line)) {
38 FixTimeZone(line);
39 timezone_ = line;
40 }
41 }
42 #endif
43 }
44
FixTimeZone(std::string & line)45 void MiscService::FixTimeZone(std::string& line) {
46 auto slashpos = line.find("/");
47 // "/" will be treated as separator, change it !
48 if (slashpos != std::string::npos) {
49 line.replace(slashpos, 1, "!");
50 }
51 }
52
SetTimeZone(std::string timezone)53 void MiscService::SetTimeZone(std::string timezone) {
54 FixTimeZone(timezone);
55 timezone_ = timezone;
56 }
57
InitializeCommandHandlers()58 std::vector<CommandHandler> MiscService::InitializeCommandHandlers() {
59 std::vector<CommandHandler> command_handlers = {
60 /* initializeCallback */
61 CommandHandler("E0Q0V1",
62 [this](const Client& client) {
63 this->HandleCommandDefaultSupported(client);
64 }),
65 CommandHandler("S0=0",
66 [this](const Client& client) {
67 this->HandleCommandDefaultSupported(client);
68 }),
69 CommandHandler("+CMEE=1",
70 [this](const Client& client) {
71 this->HandleCommandDefaultSupported(client);
72 }),
73 CommandHandler("+CMOD=0",
74 [this](const Client& client) {
75 this->HandleCommandDefaultSupported(client);
76 }),
77 CommandHandler("+CSSN=0,1",
78 [this](const Client& client) {
79 this->HandleCommandDefaultSupported(client);
80 }),
81 CommandHandler("+COLP=0",
82 [this](const Client& client) {
83 this->HandleCommandDefaultSupported(client);
84 }),
85 CommandHandler("+CSCS=\"HEX\"",
86 [this](const Client& client) {
87 this->HandleCommandDefaultSupported(client);
88 }),
89 CommandHandler("+CMGF=0",
90 [this](const Client& client) {
91 this->HandleCommandDefaultSupported(client);
92 }),
93
94 CommandHandler("+CGSN",
95 [this](const Client& client, std::string& cmd) {
96 this->HandleGetIMEI(client, cmd);
97 }),
98 };
99 return (command_handlers);
100 }
101
HandleGetIMEI(const Client & client,std::string & command)102 void MiscService::HandleGetIMEI(const Client& client, std::string& command) {
103 const std::string identityGsmImei = "867400022047199";
104 const std::string identityGsmSvn = "01";
105 const std::string information = "modem simulator";
106
107 std::vector<std::string> responses;
108
109 if (command == "AT+CGSN") {
110 responses.push_back(identityGsmImei);
111 } else {
112 CommandParser cmd(command);
113 cmd.SkipPrefix();
114 int snt = cmd.GetNextInt();
115 switch (snt) {
116 case 0: // SN: IMEI and more information provided by manufacturers
117 responses.push_back(identityGsmImei + information);
118 break;
119 case 1: // IMEI
120 responses.push_back(identityGsmImei);
121 break;
122 case 2: // IMEI and software version number
123 responses.push_back(identityGsmImei + identityGsmSvn);
124 break;
125 case 3: // Software version number
126 responses.push_back(identityGsmSvn);
127 break;
128 default: // Default IMEI
129 responses.push_back(identityGsmImei);
130 break;
131 }
132 }
133
134 responses.push_back("OK");
135 client.SendCommandResponse(responses);
136 }
137
TimeUpdate()138 void MiscService::TimeUpdate() {
139 auto now = std::time(0);
140
141 auto local_time = *std::localtime(&now);
142 auto gm_time = *std::gmtime(&now);
143
144 auto t_local_time = std::mktime(&local_time);
145 auto t_gm_time = std::mktime(&gm_time);
146
147 // Timezone offset is in number of quarter-hours
148 auto tzdiff = (int)std::difftime(t_local_time, t_gm_time) / (15 * 60);
149
150 std::stringstream ss;
151 ss << "%CTZV: \"" << std::setfill('0') << std::setw(2)
152 << local_time.tm_year % 100 << "/" << std::setfill('0') << std::setw(2)
153 << local_time.tm_mon + 1 << "/" << std::setfill('0') << std::setw(2)
154 << local_time.tm_mday << "," << std::setfill('0') << std::setw(2)
155 << local_time.tm_hour << ":" << std::setfill('0') << std::setw(2)
156 << local_time.tm_min << ":" << std::setfill('0') << std::setw(2)
157 << local_time.tm_sec << (tzdiff >= 0 ? '+' : '-')
158 << (tzdiff >= 0 ? tzdiff : -tzdiff) << ":" << local_time.tm_isdst;
159 if (!timezone_.empty()) {
160 ss << ":" << timezone_;
161 }
162 ss << "\"";
163
164 SendUnsolicitedCommand(ss.str());
165 }
166
167 } // namespace cuttlefish
168