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