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