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/data_service.h"
17
18 #include <android-base/strings.h>
19
20 #include "host/commands/modem_simulator/device_config.h"
21
22 namespace cuttlefish {
23
DataService(int32_t service_id,ChannelMonitor * channel_monitor,ThreadLooper * thread_looper)24 DataService::DataService(int32_t service_id, ChannelMonitor* channel_monitor,
25 ThreadLooper* thread_looper)
26 : ModemService(service_id, this->InitializeCommandHandlers(),
27 channel_monitor, thread_looper) {
28 InitializeServiceState();
29 }
30
InitializeCommandHandlers()31 std::vector<CommandHandler> DataService::InitializeCommandHandlers() {
32 std::vector<CommandHandler> command_handlers = {
33 CommandHandler("+CGACT=",
34 [this](const Client& client, std::string& cmd) {
35 this->HandleActivateDataCall(client, cmd);
36 }),
37 CommandHandler("+CGACT?",
38 [this](const Client& client) {
39 this->HandleQueryDataCallList(client);
40 }),
41 CommandHandler("+CGDCONT=",
42 [this](const Client& client, std::string& cmd) {
43 this->HandlePDPContext(client, cmd);
44 }),
45 CommandHandler("+CGDCONT?",
46 [this](const Client& client) {
47 this->HandleQueryPDPContextList(client);
48 }),
49 CommandHandler("+CGQREQ=1",
50 [this](const Client& client) {
51 this->HandleCommandDefaultSupported(client);
52 }),
53 CommandHandler("+CGQMIN=1",
54 [this](const Client& client) {
55 this->HandleCommandDefaultSupported(client);
56 }),
57 CommandHandler("+CGEREP=1,0",
58 [this](const Client& client) {
59 this->HandleCommandDefaultSupported(client);
60 }),
61 CommandHandler("+CGDATA",
62 [this](const Client& client, std::string& cmd) {
63 this->HandleEnterDataState(client, cmd);
64 }),
65 CommandHandler("D*99***1#",
66 [this](const Client& client) {
67 this->HandleCommandDefaultSupported(client);
68 }),
69 CommandHandler("+CGCONTRDP",
70 [this](const Client& client, std::string& cmd) {
71 this->HandleReadDynamicParam(client, cmd);
72 }),
73 };
74 return (command_handlers);
75 }
76
InitializeServiceState()77 void DataService::InitializeServiceState() {
78 // Initialize data connection config
79 }
80
81 /**
82 * AT+CGACT
83 * The execution command is used to activate or deactivate the specified PDP
84 * context(s).
85 *
86 * Command Possible response(s)
87 * +CGACT=[<state>[,<cid> OK
88 * [,<cid>[,...]]]] +CME ERROR: <err>
89 * +CGACT? [+CGACT: <cid>,<state>]
90 * [<CR><LF>+CGACT: <cid>,<state>[...]]
91 * <state>: integer type; indicates the state of PDP context activation.
92 * 0: deactivated
93 * 1: activated
94 * <cid>: (PDP Context Identifier) integer(1~15), specifies the PDP context ID.
95 *
96 * see RIL_REQUEST_SETUP_DATA_CALL in RIL
97 */
HandleActivateDataCall(const Client & client,const std::string &)98 void DataService::HandleActivateDataCall(const Client& client,
99 const std::string& /*command*/) {
100 client.SendCommandResponse("OK");
101 }
102
103 /**
104 * see AT+CGACT
105 */
HandleQueryDataCallList(const Client & client)106 void DataService::HandleQueryDataCallList(const Client& client) {
107 std::vector<std::string> responses;
108
109 std::stringstream ss;
110 for (auto iter = pdp_context_.begin(); iter != pdp_context_.end(); ++iter) {
111 if (iter->state == PDPContext::ACTIVE) {
112 ss.clear();
113 ss << "+CGACT: " << iter->cid << "," << iter->state;
114 responses.push_back(ss.str());
115 ss.str("");
116 }
117 }
118 responses.push_back("OK");
119 client.SendCommandResponse(responses);
120 }
121
122 /**
123 * AT+CGDCONT
124 * The set command specifies PDP context parameter values for a PDP context
125 * identified by the (local) context identification parameter, <cid>.
126 *
127 * Command Possible response(s)
128 * +CGDCONT=[<cid>[,<PDP_type>[,<APN> OK
129 * [,<PDP_addr>[,<d_comp> [,<h_comp>] +CME ERROR: <err>
130 * ]]]]]
131 * +CGDCONT? +CGDCONT: <cid>,<pdp_type>,<APN>,
132 * <pdp_addr>,<d_comp>,<h_comp><CR><LF>
133 * [+CGDCONT: <cid>,<pdp_type>,<APN>,
134 * <pdp_addr>,<d_comp>,<h_comp><CR><LF>[...]]
135 * OK
136 * <cid>: see AT+CGACT
137 * <PDP_type>: string type; specifies the type of packet data protocol.
138 * Value: X.25, IP, IPV6, IPV4V6, OSPIH, PPP, Non-IP,Ethernet
139 * <APN>: string type; a logical name that is used to select the GGSN or the
140 * external packet data network.If the value is null or omitted, then
141 * the subscription value will be requested
142 * <PDP_addr>: string type; identifies the MT in the address space applicable
143 * to the PDP
144 * <d_comp>: integer type; controls PDP data compression
145 * <h_comp>: integer type; controls PDP header compression
146 *
147 * see RIL_REQUEST_SETUP_DATA_CALL in RIL
148 */
HandlePDPContext(const Client & client,const std::string & command)149 void DataService::HandlePDPContext(const Client& client,
150 const std::string& command) {
151 CommandParser cmd(command);
152 cmd.SkipPrefix(); /* skip +CGDCONT= */
153 int cid = cmd.GetNextInt();
154
155 std::string ip_type(cmd.GetNextStr(','));
156 std::string apn(cmd.GetNextStr(','));
157
158 auto address = cuttlefish::modem::DeviceConfig::ril_address_and_prefix();
159 auto dnses = cuttlefish::modem::DeviceConfig::ril_dns();
160 auto gateways = cuttlefish::modem::DeviceConfig::ril_gateway();
161
162 PDPContext pdp_context = {cid,
163 PDPContext::ACTIVE,
164 ip_type, // IPV4 or IPV6 or IPV4V6
165 apn,
166 address,
167 dnses,
168 gateways};
169
170 // check cid
171 auto iter = pdp_context_.begin();
172 for (; iter != pdp_context_.end(); ++iter) {
173 if (pdp_context.cid == iter->cid) {
174 *iter = pdp_context;
175 break;
176 }
177 }
178
179 if (iter == pdp_context_.end()) {
180 pdp_context_.push_back(pdp_context);
181 }
182
183 client.SendCommandResponse("OK");
184 }
185
186 /**
187 * see AT+CGDCONT above
188 */
HandleQueryPDPContextList(const Client & client)189 void DataService::HandleQueryPDPContextList(const Client& client) {
190 std::vector<std::string> responses;
191
192 std::stringstream ss;
193 for (auto it = pdp_context_.begin(); it != pdp_context_.end(); ++it) {
194 std::stringstream ss;
195 ss << "+CGDCONT: " << it->cid << "," << it->conn_types << ","
196 << it->apn << "," << it->addresses << ",0,0";
197 responses.push_back(ss.str());
198 }
199 responses.push_back("OK");
200 client.SendCommandResponse(responses);
201 }
202
203 /**
204 * AT+CGDATA
205 * The execution command causes the MT to perform whatever actions are
206 * necessary to establish communication between the TE and the network using
207 * one or more Packet Domain PDP types.
208 *
209 * Command Possible response(s)
210 * +CGDATA[=<L2P>[,[,<cid> CONNECT
211 * [,...]]]] ERROR
212 * +CME ERROR: <err>
213 *
214 * <L2P>: string type; indicates the layer 2 protocol to be used between the
215 * TE and MT NULL none, for PDP type OSP:IHOSS (Obsolete)
216 * value: PPP, PAD, X25, M-xxxx
217 * <cid>: see AT+CGACT
218 *
219 * see RIL_REQUEST_SETUP_DATA_CALL in RIL
220 */
HandleEnterDataState(const Client & client,const std::string & command)221 void DataService::HandleEnterDataState(const Client& client,
222 const std::string& command) {
223 std::string response;
224
225 CommandParser cmd(command);
226 cmd.SkipPrefix();
227 cmd.SkipComma();
228 int cid = cmd.GetNextInt();
229
230 // Check cid
231 auto iter = pdp_context_.begin();
232 for (; iter != pdp_context_.end(); ++iter) {
233 if (cid == iter->cid && iter->state == PDPContext::ACTIVE) {
234 response = "CONNECT";
235 break;
236 }
237 }
238
239 if (iter == pdp_context_.end()) {
240 response = "ERROR";
241 }
242
243 client.SendCommandResponse(response);
244 }
245
246 /**
247 * AT+CGCONTRDP
248 * The execution command returns the relevant information for an active non
249 * secondary PDP context with the context identifier <cid>.
250 *
251 * Command Possible response(s)
252 * +CGCONTRDP[=<cid>] [+CGCONTRDP: <cid>,<bearer_id>,<apn>
253 * [,<local_addr and subnet_mask>[,<gw_addr>
254 * [,<DNS_prim_addr>[<DNS_sec_addr>[...]]]]]]
255 * [<CR><LF>+CGCONTRDP: <cid>,<bearer_id>,<apn>
256 * [,<local_addr and subnet_mask>[,<gw_addr>
257 * [,<DNS_prim_addr>[<DNS_sec_addr>[...]]]]]]
258 *
259 * <cid>: see AT+CGACT
260 * <bearer_id>: integer type; identifies the bearer, i.e. the EPS bearer and
261 * the NSAPI.
262 * <local_addr and subnet_mask>: string type; shows the IP address and subnet
263 * mask of the MT.
264 * <gw_addr>: string type; shows the Gateway Address of the MT. The string is
265 * given as dot-separated numeric (0-255) parameters.
266 * <DNS_prim_addr>: string type; shows the IP address of the primary DNS server.
267 * <DNS_sec_addr>: string type; shows the IP address of the secondary DNS server.
268 *
269 *
270 * see RIL_REQUEST_SETUP_DATA_CALL in RIL
271 */
HandleReadDynamicParam(const Client & client,const std::string & command)272 void DataService::HandleReadDynamicParam(const Client& client,
273 const std::string& command) {
274 std::vector<std::string> responses;
275
276 CommandParser cmd(command);
277 cmd.SkipPrefix(); /* skip prefix AT+CGCONTRDP= */
278
279 int cid = cmd.GetNextInt();
280 auto iter = pdp_context_.begin(); // Check cid
281 for (; iter != pdp_context_.end(); ++iter) {
282 if (cid == iter->cid && iter->state == PDPContext::ACTIVE) {
283 break;
284 }
285 }
286
287 if (iter == pdp_context_.end()) {
288 responses.push_back(kCmeErrorInvalidIndex); // number
289 } else {
290 std::stringstream ss;
291 ss << "+CGCONTRDP: "
292 << iter->cid << ",5,"
293 << iter->apn << ","
294 << iter->addresses << ","
295 << iter->gateways << ","
296 << iter->dnses;
297 responses.push_back(ss.str());
298 responses.push_back("OK");
299 }
300
301 client.SendCommandResponse(responses);
302 }
303
sendOnePhysChanCfgUpdate(int status,int bandwidth,int rat,int freq,int id)304 void DataService::sendOnePhysChanCfgUpdate(int status, int bandwidth, int rat,
305 int freq, int id) {
306 std::stringstream ss;
307 ss << "%CGFPCCFG: " << status << "," << bandwidth << "," << rat << "," << freq
308 << "," << id;
309 SendUnsolicitedCommand(ss.str());
310 }
311
onUpdatePhysicalChannelconfigs(int modem_tech,int freq,int cellBandwidthDownlink)312 void DataService::onUpdatePhysicalChannelconfigs(int modem_tech, int freq,
313 int cellBandwidthDownlink) {
314 updatePhysicalChannelconfigs(modem_tech, freq, cellBandwidthDownlink, 3);
315 }
316
updatePhysicalChannelconfigs(int modem_tech,int freq,int cellBandwidthDownlink,int count)317 void DataService::updatePhysicalChannelconfigs(int modem_tech, int freq,
318 int cellBandwidthDownlink,
319 int count) {
320 if (count <= 0) {
321 return;
322 }
323
324 const int PRIMARY_SERVING = 1;
325 const int SECONDARY_SERVING = 2;
326
327 for (const auto& iter : pdp_context_) {
328 if (iter.state == PDPContext::ACTIVE) {
329 sendOnePhysChanCfgUpdate(PRIMARY_SERVING, cellBandwidthDownlink,
330 modem_tech, freq, iter.cid);
331 sendOnePhysChanCfgUpdate(SECONDARY_SERVING, cellBandwidthDownlink,
332 modem_tech, freq, iter.cid);
333 }
334 }
335
336 // call again after 1 sec delay
337 count--;
338 thread_looper_->PostWithDelay(
339 std::chrono::seconds(1),
340 makeSafeCallback(this, &DataService::updatePhysicalChannelconfigs,
341 modem_tech, freq, cellBandwidthDownlink, count));
342 }
343
344 } // namespace cuttlefish
345