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