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/stk_service.h"
17
18 #include <android-base/logging.h>
19 namespace cuttlefish {
20
StkService(int32_t service_id,ChannelMonitor * channel_monitor,ThreadLooper * thread_looper)21 StkService::StkService(int32_t service_id, ChannelMonitor* channel_monitor,
22 ThreadLooper* thread_looper)
23 : ModemService(service_id, this->InitializeCommandHandlers(),
24 channel_monitor, thread_looper) {}
25
InitializeCommandHandlers()26 std::vector<CommandHandler> StkService::InitializeCommandHandlers() {
27 std::vector<CommandHandler> command_handlers = {
28 CommandHandler("+CUSATD?",
29 [this](const Client& client) {
30 this->HandleReportStkServiceIsRunning(client);
31 }),
32 CommandHandler("+CUSATE=",
33 [this](const Client& client, std::string& cmd) {
34 this->HandleSendEnvelope(client, cmd);
35 }),
36 CommandHandler("+CUSATT=",
37 [this](const Client& client, std::string& cmd) {
38 this->HandleSendTerminalResponseToSim(client, cmd);
39 }),
40 };
41 return (command_handlers);
42 }
43
SetupDependency(SimService * sim)44 void StkService::SetupDependency(SimService* sim) { sim_service_ = sim; }
45
46 /**
47 * AT+CUSATD
48 * This command determines if, and optionally which profile should be downloaded
49 * to the UICC automatically upon start-up.
50 *
51 * Command Possible response(s)
52 * +CUSATD=[<download>[,<reporting>]] +CME ERROR: <err>
53 * +CUSATD? +CUSATD: <download>,<reporting>
54 *
55 * <download>: integer type.
56 * 0 Download MT default profile automatically during next start-up.
57 * 1 Download the combined TE and MT profile
58 * 2 Halt next UICC start-up when ready for profile download.
59 * <reporting>: integer type.
60 * 0 Disable +CUSATS, i.e. no notification.
61 * 1 Enable +CUSATS, i.e. notify TE.
62 *
63 * see RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING in RIL
64 */
HandleReportStkServiceIsRunning(const Client & client)65 void StkService::HandleReportStkServiceIsRunning(const Client& client) {
66 std::vector<std::string> response = {{"+CUSATD: 0,1"}, {"OK"}};
67 client.SendCommandResponse(response);
68
69 if (!sim_service_) {
70 return;
71 }
72
73 XMLElement* root = sim_service_->GetIccProfile();
74 if (!root) {
75 return;
76 }
77
78 XMLElement *setup_menu = root->FirstChildElement("SETUPMENU");
79 auto text = setup_menu->FindAttribute("text");
80
81 std::string unsol_command = "+CUSATP:";
82 unsol_command += text ? text->Value() : "";
83 SendUnsolicitedCommand(unsol_command);
84 }
85
86 /**
87 * AT+CUSATE
88 * Execution command allows the TE to send a USAT envelope command to the MT
89 *
90 * Command Possible response(s)
91 * +CUSATE=<envelope_command> +CUSATE: <envelope_response>[,<busy>]
92 * [<CR><LF>+CUSATE2: <sw1>,<sw2>]
93 * +CME ERROR: <err>
94 *
95 * <envelope_command>: string type in hexadecimal character format.
96 * <envelope_response>: string type in hexadecimal character format.
97 * <busy>: integer type.
98 * 0 UICC indicated normal ending of the command.
99 * 1 UICC responded with USAT is busy, no retry by the MT.
100 * 2 UICC responded with USAT is busy even after one or more retries by the MT.
101 * <sw1>: integer type.
102 * <sw2>: integer type.
103 *
104 * see RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND in RIL
105 */
HandleSendEnvelope(const Client & client,std::string & command)106 void StkService::HandleSendEnvelope(const Client& client , std::string& command) {
107 std::vector<std::string> response = {{"+CUSATE: 0"}, {"OK"}};
108 client.SendCommandResponse(response);
109
110 if (!sim_service_) {
111 return;
112 }
113
114 CommandParser cmd(command);
115 cmd.SkipPrefix();
116 auto data = cmd.GetNextStr();
117 std::string menu_id(data.substr(data.size() - 2)); // get the last two char
118
119 XMLElement* root = sim_service_->GetIccProfile();
120 if (!root) {
121 return;
122 }
123
124 XMLElement* setup_menu = root->FirstChildElement("SETUPMENU");
125 if (!setup_menu) {
126 return;
127 }
128
129 auto select_item = setup_menu->FirstChildElement("SELECTITEM");
130 while (select_item) {
131 auto menu_id_attr = select_item->FindAttribute("menuId");
132 if (menu_id_attr && menu_id_attr->Value() == menu_id) {
133 break;
134 }
135 select_item = select_item->NextSiblingElement("SELECTITEM");
136 }
137 if (!select_item) {
138 LOG(ERROR) << "Not found menu id: " << menu_id;
139 return;
140 }
141
142 auto select_item_cmd = select_item->FindAttribute("cmd");
143 if (select_item_cmd) {
144 std::string cmd_str = select_item_cmd->Value();
145 if (cmd_str == "24") { // SELECT_ITEM
146 current_select_item_menu_ids_.push_back(menu_id);
147 }
148 }
149
150 std::string unsol_command = "+CUSATP:";
151 auto text = select_item->FindAttribute("text");
152 std::string text_value = text ? text->Value() : "";
153 unsol_command.append(text_value);
154 SendUnsolicitedCommand(unsol_command);
155 }
156
157 /**
158 * AT+CUSATT
159 * Execution command sends a USAT terminal response to the MT as an answer to
160 * a preceding USAT proactive command sent from the UICC with unsolicited result
161 * code +CUSATP: <proactive_command>
162 *
163 * Command Possible response(s)
164 * +CUSATT=<terminal_response> +CME ERROR: <err>
165 *
166 * <terminal_response>: string type in hexadecimal character format.
167 *
168 * see RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE in RIL
169 */
HandleSendTerminalResponseToSim(const Client & client,std::string & command)170 void StkService::HandleSendTerminalResponseToSim(const Client& client, std::string& command) {
171 std::vector<std::string> response = {{"+CUSATT: 0"}, {"OK"}};
172 client.SendCommandResponse(response);
173
174 OnUnsolicitedCommandForTR(command);
175 }
176
GetCurrentSelectItem()177 XMLElement* StkService::GetCurrentSelectItem() {
178 if (!sim_service_) {
179 return nullptr;
180 }
181
182 XMLElement *root = sim_service_->GetIccProfile();
183 if (!root) {
184 current_select_item_menu_ids_.clear();
185 return nullptr;
186 }
187
188 XMLElement *menu = root->FirstChildElement("SETUPMENU");
189 if (!menu) {
190 current_select_item_menu_ids_.clear();
191 return nullptr;
192 }
193
194 /**
195 * e.g. current_select_item_menu_ids_: {"1", "02"}
196 * <SELECTITEM id="1">
197 * <SELECTITEM id="01">
198 * </SELECTITEM>
199 * <SELECTITEM id="02">
200 * </SELECTITEM>
201 * </SELECTITEM>
202 */
203 XMLElement* select_item = nullptr;
204 auto iter = current_select_item_menu_ids_.begin();
205 for (; iter != current_select_item_menu_ids_.end(); ++iter) {
206 select_item = menu->FirstChildElement("SELECTITEM");
207 while (select_item) {
208 auto menu_id_attr = select_item->FindAttribute("menuId");
209 if (menu_id_attr && menu_id_attr->Value() == *iter) {
210 auto menu_id_str = menu_id_attr->Value();
211 if (menu_id_str == *iter) {
212 break;
213 }
214 }
215 select_item = select_item->NextSiblingElement("SELECTITEM");
216 }
217 if (!select_item) {
218 break;
219 }
220 menu = select_item;
221 }
222
223 return select_item;
224 }
225
OnUnsolicitedCommandForTR(std::string & command)226 void StkService::OnUnsolicitedCommandForTR(std::string& command) {
227 CommandParser cmd(command);
228 cmd.SkipPrefix();
229 auto data = cmd.GetNextStr();
230 auto menu_id = data.substr(data.size() - 2);
231
232 // '10': UICC_SESSION_TERM_BY_USER
233 // '12': NO_RESPONSE_FROM_USER
234 if (menu_id == "10" || menu_id == "12") {
235 current_select_item_menu_ids_.clear();
236 SendUnsolicitedCommand("+CUSATEND");
237 return;
238 }
239
240 XMLElement *select_item = GetCurrentSelectItem();
241 if (!select_item) {
242 current_select_item_menu_ids_.clear();
243 SendUnsolicitedCommand("+CUSATEND");
244 return;
245 }
246
247 if (menu_id == "11") { // BACKWARD_MOVE_BY_USER
248 current_select_item_menu_ids_.pop_back();
249 if (current_select_item_menu_ids_.size() >= 1) {
250 select_item = GetCurrentSelectItem();
251 auto text = select_item->FindAttribute("text");
252 if (text) {
253 std::string unsol_command = "+CUSATP: ";
254 unsol_command += text->Value();
255 SendUnsolicitedCommand(unsol_command);
256 }
257 } else {
258 SendUnsolicitedCommand("+CUSATEND");
259 }
260 return;
261 } else if (menu_id == "00") { // OK
262 auto text = select_item->FindAttribute("text");
263 if (text) {
264 std::string unsol_command = "+CUSATP: ";
265 unsol_command += text->Value();
266 SendUnsolicitedCommand(unsol_command);
267 }
268 return;
269 }
270
271 auto final = select_item->FirstChildElement();
272 while (final) {
273 auto attr = final->FindAttribute("menuId");
274 if (attr && attr->Value() == menu_id) {
275 std::string attr_value = attr->Value();
276 if (attr_value == menu_id) {
277 break;
278 }
279 }
280 final = final->NextSiblingElement();
281 }
282 if (!final) {
283 current_select_item_menu_ids_.clear();
284 SendUnsolicitedCommand("+CUSATEND");
285 return;
286 }
287
288 auto cmd_attr = final->FindAttribute("cmd");
289 if (cmd_attr) {
290 std::string cmd_attr_str = cmd_attr->Value();
291 if (cmd_attr_str == "24") {
292 std::string menu_id_str(menu_id);
293 current_select_item_menu_ids_.push_back(menu_id_str);
294 }
295 }
296 auto text = final->FindAttribute("text");
297 std::string unsol_command = "+CUSATP:";
298 unsol_command += text ? text->Value() : "";
299 SendUnsolicitedCommand(unsol_command);
300 }
301
302 } // namespace cuttlefish
303