• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/sim_service.h"
17 
18 #include <android-base/logging.h>
19 #include <tinyxml2.h>
20 
21 #include "common/libs/utils/files.h"
22 #include "host/commands/modem_simulator/device_config.h"
23 #include "host/commands/modem_simulator/network_service.h"
24 #include "host/commands/modem_simulator/pdu_parser.h"
25 #include "host/commands/modem_simulator/nvram_config.h"
26 
27 namespace cuttlefish {
28 
29 const std::pair<int, int> kSimPinSizeRange(4, 8);
30 constexpr int kSimPukSize = 8;
31 constexpr int kSimPinMaxRetryTimes = 3;
32 constexpr int kSimPukMaxRetryTimes = 10;
33 static const std::string kDefaultPinCode = "1234";
34 static const std::string kDefaultPukCode = "12345678";
35 
36 static const std::string MF_SIM       = "3F00";
37 static const std::string DF_TELECOM   = "7F10";
38 static const std::string DF_PHONEBOOK = "5F3A";
39 static const std::string DF_GRAPHICS  = "5F50";
40 static const std::string DF_GSM       = "7F20";
41 static const std::string DF_CDMA      = "7F25";
42 static const std::string DF_ADF       = "7FFF";  // UICC access
43 
44 // In an ADN record, everything but the alpha identifier
45 // is in a footer that's 14 bytes
46 constexpr int kFooterSizeBytes = 14;
47 // Maximum size of the un-extended number field
48 constexpr int kMaxNumberSizeBytes = 11;
49 
50 constexpr int kMaxLogicalChannels = 3;
51 
52 const std::map<SimService::SimStatus, std::string> gSimStatusResponse = {
53     {SimService::SIM_STATUS_ABSENT,     ModemService::kCmeErrorSimNotInserted},
54     {SimService::SIM_STATUS_NOT_READY,  ModemService::kCmeErrorSimBusy},
55     {SimService::SIM_STATUS_READY,      "+CPIN: READY"},
56     {SimService::SIM_STATUS_PIN,        "+CPIN: SIM PIN"},
57     {SimService::SIM_STATUS_PUK,        "+CPIN: SIM PUK"},
58 };
59 
60 /* SimFileSystem */
GetRootElement()61 XMLElement* SimService::SimFileSystem::GetRootElement() {
62   return doc.RootElement();
63 }
64 
GetCommonIccEFPath(EFId efid)65 std::string SimService::SimFileSystem::GetCommonIccEFPath(EFId efid) {
66   switch (efid) {
67     case EF_ADN:
68     case EF_FDN:
69     case EF_MSISDN:
70     case EF_SDN:
71     case EF_EXT1:
72     case EF_EXT2:
73     case EF_EXT3:
74     case EF_PSI:
75       return MF_SIM + DF_TELECOM;
76 
77     case EF_ICCID:
78     case EF_PL:
79       return MF_SIM;
80     case EF_PBR:
81       // we only support global phonebook.
82       return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
83     case EF_IMG:
84       return MF_SIM + DF_TELECOM + DF_GRAPHICS;
85     default:
86       return {};
87   }
88 }
89 
GetUsimEFPath(EFId efid)90 std::string SimService::SimFileSystem::GetUsimEFPath(EFId efid) {
91   switch(efid) {
92     case EF_SMS:
93     case EF_EXT5:
94     case EF_EXT6:
95     case EF_MWIS:
96     case EF_MBI:
97     case EF_SPN:
98     case EF_AD:
99     case EF_MBDN:
100     case EF_PNN:
101     case EF_OPL:
102     case EF_SPDI:
103     case EF_SST:
104     case EF_CFIS:
105     case EF_MAILBOX_CPHS:
106     case EF_VOICE_MAIL_INDICATOR_CPHS:
107     case EF_CFF_CPHS:
108     case EF_SPN_CPHS:
109     case EF_SPN_SHORT_CPHS:
110     case EF_FDN:
111     case EF_SDN:
112     case EF_EXT3:
113     case EF_MSISDN:
114     case EF_EXT2:
115     case EF_INFO_CPHS:
116     case EF_CSP_CPHS:
117     case EF_GID1:
118     case EF_GID2:
119     case EF_LI:
120     case EF_PLMN_W_ACT:
121     case EF_OPLMN_W_ACT:
122     case EF_HPLMN_W_ACT:
123     case EF_EHPLMN:
124     case EF_FPLMN:
125     case EF_LRPLMNSI:
126     case EF_HPPLMN:
127       return MF_SIM + DF_ADF;
128 
129     case EF_PBR:
130       // we only support global phonebook.
131       return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
132     default:
133       std::string path = GetCommonIccEFPath(efid);
134       if (path.empty()) {
135         // The EFids in USIM phone book entries are decided by the card manufacturer.
136         // So if we don't match any of the cases above and if it's a USIM return
137         // the phone book path.
138         return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
139       }
140       return path;
141   }
142 }
143 
FindAttribute(XMLElement * parent,const std::string & attr_name,const std::string & attr_value)144 XMLElement* SimService::SimFileSystem::FindAttribute(XMLElement *parent,
145                                                      const std::string& attr_name,
146                                                      const std::string& attr_value) {
147   if (parent == nullptr) {
148     return nullptr;
149   }
150 
151   XMLElement* child = parent->FirstChildElement();
152   while (child) {
153     const XMLAttribute *attr = child->FindAttribute(attr_name.c_str());
154     if (attr && attr->Value() == attr_value) {
155       break;
156     }
157     child = child->NextSiblingElement();
158   }
159   return child;
160 };
161 
AppendNewElement(XMLElement * parent,const char * name)162 XMLElement* SimService::SimFileSystem::AppendNewElement(XMLElement* parent,
163                                                         const char* name) {
164   auto element = doc.NewElement(name);
165   parent->InsertEndChild(element);
166   return element;
167 }
168 
AppendNewElementWithText(XMLElement * parent,const char * name,const char * text)169 XMLElement* SimService::SimFileSystem::AppendNewElementWithText(
170         XMLElement* parent, const char* name, const char* text) {
171   auto element = doc.NewElement(name);
172   auto xml_text = doc.NewText(text);
173   element->InsertEndChild(xml_text);
174   parent->InsertEndChild(element);
175   return element;
176 }
177 
178 /* PinStatus */
CheckPasswordValid(std::string_view password)179 bool SimService::PinStatus::CheckPasswordValid(std::string_view password) {
180   for (int i = 0; i < password.size(); i++) {
181     int c = (int)password[i];
182     if (c >= 48 && c <= 57) {
183       continue;
184     } else {
185       return false;
186     }
187   }
188   return true;
189 }
190 
VerifyPIN(const std::string_view pin)191 bool SimService::PinStatus::VerifyPIN(const std::string_view pin) {
192   if (pin.size() < kSimPinSizeRange.first || pin.size() > kSimPinSizeRange.second) {
193     return false;
194   }
195 
196   if (!CheckPasswordValid(pin)) {
197     return false;
198   }
199 
200   if (pin_remaining_times_ <= 0) {
201     return false;
202   }
203 
204   std::string_view temp(pin_);
205   if (pin == temp) {  // C++20 remove Operator!=
206     pin_remaining_times_ = kSimPinMaxRetryTimes;
207     return true;
208   }
209 
210   pin_remaining_times_ -= 1;
211   return false;
212 }
213 
VerifyPUK(const std::string_view puk)214 bool SimService::PinStatus::VerifyPUK(const std::string_view puk) {
215   if (puk.size() != kSimPukSize) {
216     return false;
217   }
218 
219   if (!CheckPasswordValid(puk)) {
220     return false;
221   }
222 
223   if (puk_remaining_times_ <= 0) {
224     return false;
225   }
226 
227   std::string_view temp(puk_);
228   if (puk == temp) {  // C++20 remove Operator!=
229     pin_remaining_times_ = kSimPinMaxRetryTimes;
230     puk_remaining_times_ = kSimPukMaxRetryTimes;
231     return true;
232   }
233 
234   puk_remaining_times_ -= 1;
235   return false;
236 }
237 
ChangePIN(ChangeMode mode,const std::string_view pin_or_puk,const std::string_view new_pin)238 bool SimService::PinStatus::ChangePIN(ChangeMode mode,
239                                       const std::string_view pin_or_puk,
240                                       const std::string_view new_pin) {
241   auto length = new_pin.length();
242   if (length < kSimPinSizeRange.first || length > kSimPinSizeRange.second) {
243     LOG(ERROR) << "Invalid digit number for PIN";
244     return false;
245   }
246 
247   bool result = false;
248   if (mode == WITH_PIN) {  // using old pin to change pin
249     result = VerifyPIN(pin_or_puk);
250   } else if (mode == WITH_PUK) {  // using puk to change pin
251     result = VerifyPUK(pin_or_puk);
252   }
253 
254   if (!result) {
255     LOG(ERROR) << "Incorrect PIN or PUK";
256     return false;
257   }
258 
259   if (!CheckPasswordValid(new_pin)) {
260     return false;
261   }
262 
263   std::string temp(new_pin);
264   pin_ = temp;
265   return true;
266 }
267 
ChangePUK(const std::string_view puk,const std::string_view new_puk)268 bool SimService::PinStatus::ChangePUK(const std::string_view puk,
269                                       const std::string_view new_puk) {
270   bool result = VerifyPUK(puk);
271   if (!result) {
272     LOG(ERROR) << "Incorrect PUK or no retry times";
273     return false;
274   }
275 
276   if (new_puk.length() != kSimPukSize) {
277     LOG(ERROR) << "Invalid digit number for PUK";
278     return false;
279   }
280 
281   std::string temp(new_puk);
282   puk_ = temp;
283   return true;
284 };
285 
SimService(int32_t service_id,ChannelMonitor * channel_monitor,ThreadLooper * thread_looper)286 SimService::SimService(int32_t service_id, ChannelMonitor* channel_monitor,
287                        ThreadLooper* thread_looper)
288     : ModemService(service_id, this->InitializeCommandHandlers(),
289                    channel_monitor, thread_looper) {
290   InitializeServiceState();
291 }
292 
InitializeCommandHandlers()293 std::vector<CommandHandler> SimService::InitializeCommandHandlers() {
294   std::vector<CommandHandler> command_handlers = {
295       CommandHandler(
296           "+CPIN?",
297           [this](const Client& client) { this->HandleSIMStatusReq(client); }),
298       CommandHandler("+CPIN=",
299                      [this](const Client& client, std::string& cmd) {
300                        this->HandleChangeOrEnterPIN(client, cmd);
301                      }),
302       CommandHandler("+CRSM=",
303                      [this](const Client& client, std::string& cmd) {
304                        this->HandleSIM_IO(client, cmd);
305                      }),
306       CommandHandler("+CSIM=",
307                      [this](const Client& client, std::string& cmd) {
308                      this->HandleCSIM_IO(client, cmd);
309                      }),
310       CommandHandler(
311           "+CIMI",
312           [this](const Client& client) { this->HandleGetIMSI(client); }),
313       CommandHandler(
314           "+CICCID",
315           [this](const Client& client) { this->HandleGetIccId(client); }),
316       CommandHandler("+CLCK=",
317                      [this](const Client& client, std::string& cmd) {
318                        this->HandleFacilityLock(client, cmd);
319                      }),
320       CommandHandler("+CCHO=",
321                      [this](const Client& client, std::string& cmd) {
322                        this->HandleOpenLogicalChannel(client, cmd);
323                      }),
324       CommandHandler("+CCHC=",
325                      [this](const Client& client, std::string& cmd) {
326                        this->HandleCloseLogicalChannel(client, cmd);
327                      }),
328       CommandHandler("+CGLA=",
329                      [this](const Client& client, std::string& cmd) {
330                        this->HandleTransmitLogicalChannel(client, cmd);
331                      }),
332       CommandHandler("+CPWD=",
333                      [this](const Client& client, std::string& cmd) {
334                        this->HandleChangePassword(client, cmd);
335                      }),
336       CommandHandler("+CPINR=",
337                      [this](const Client& client, std::string& cmd) {
338                        this->HandleQueryRemainTimes(client, cmd);
339                      }),
340       CommandHandler("+CCSS",
341                      [this](const Client& client, std::string& cmd) {
342                        this->HandleCdmaSubscriptionSource(client, cmd);
343                      }),
344       CommandHandler("+WRMP",
345                      [this](const Client& client, std::string& cmd) {
346                        this->HandleCdmaRoamingPreference(client, cmd);
347                      }),
348       CommandHandler("^MBAU=",
349                     [this](const Client& client, std::string& cmd) {
350                     this->HandleSimAuthentication(client, cmd);
351                     }),
352   };
353   return (command_handlers);
354 }
355 
InitializeServiceState()356 void SimService::InitializeServiceState() {
357   InitializeSimFileSystemAndSimState();
358 
359   InitializeFacilityLock();
360 
361   // Max logical channels: 3
362   logical_channels_ = {
363       LogicalChannel(1), LogicalChannel(2), LogicalChannel(kMaxLogicalChannels),
364   };
365 }
366 
InitializeSimFileSystemAndSimState()367 void SimService::InitializeSimFileSystemAndSimState() {
368   auto nvram_config = NvramConfig::Get();
369   auto sim_type = nvram_config->sim_type();
370   std::stringstream ss;
371   if (sim_type == 2) {  // Special sim card for CtsCarrierApiTestCases
372     ss << "iccprofile_for_sim" << service_id_ << "_for_CtsCarrierApiTestCases.xml";
373   } else {
374     ss << "iccprofile_for_sim" << service_id_ << ".xml";
375   }
376   auto icc_profile_name = ss.str();
377 
378   auto icc_profile_path = cuttlefish::modem::DeviceConfig::PerInstancePath(
379       icc_profile_name.c_str());
380   std::string file = icc_profile_path;
381 
382   if (!cuttlefish::FileExists(icc_profile_path) ||
383       !cuttlefish::FileHasContent(icc_profile_path.c_str())) {
384     ss.clear();
385     ss.str("");
386 
387     if (sim_type == 2) {  // Special sim card for CtsCarrierApiTestCases
388       ss << "etc/modem_simulator/files/iccprofile_for_sim" << service_id_
389           << "_for_CtsCarrierApiTestCases.xml";
390     } else {
391       ss << "etc/modem_simulator/files/iccprofile_for_sim" << service_id_ << ".xml";
392     }
393 
394     auto etc_file_path =
395         cuttlefish::modem::DeviceConfig::DefaultHostArtifactsPath(ss.str());
396     if (!cuttlefish::FileExists(etc_file_path) || !cuttlefish::FileHasContent(etc_file_path)) {
397       sim_status_ = SIM_STATUS_ABSENT;
398       return;
399     }
400     file = etc_file_path;
401   }
402 
403   sim_file_system_.file_path = icc_profile_path;
404   auto err = sim_file_system_.doc.LoadFile(file.c_str());
405   if (err != tinyxml2::XML_SUCCESS) {
406     LOG(ERROR) << "Unable to load XML file '" << file << " ', error " << err;
407     sim_status_ = SIM_STATUS_ABSENT;
408     return;
409   }
410 
411   XMLElement *root = sim_file_system_.GetRootElement();
412   if (!root) {
413     LOG(ERROR) << "Unable to find root element: IccProfile";
414     sim_status_ = SIM_STATUS_ABSENT;
415     return;
416   }
417 
418   // Default value if iccprofile not configure pin state
419   sim_status_ = SIM_STATUS_READY;
420   pin1_status_.pin_ = kDefaultPinCode;
421   pin1_status_.puk_ = kDefaultPukCode;
422   pin1_status_.pin_remaining_times_ = kSimPinMaxRetryTimes;
423   pin1_status_.puk_remaining_times_ = kSimPukMaxRetryTimes;
424   pin2_status_.pin_ = kDefaultPinCode;
425   pin2_status_.puk_ = kDefaultPukCode;
426   pin2_status_.pin_remaining_times_ = kSimPinMaxRetryTimes;
427   pin2_status_.puk_remaining_times_ = kSimPukMaxRetryTimes;
428 
429   XMLElement *pin_profile = root->FirstChildElement("PinProfile");
430   if (pin_profile) {
431     // Pin1 status
432     auto pin_state = pin_profile->FirstChildElement("PINSTATE");
433     if (pin_state) {
434       std::string state = pin_state->GetText();
435       if (state == "PINSTATE_ENABLED_NOT_VERIFIED") {
436         sim_status_ = SIM_STATUS_PIN;
437       } else if (state == "PINSTATE_ENABLED_BLOCKED") {
438         sim_status_ = SIM_STATUS_PUK;
439       }
440     }
441     auto pin_code = pin_profile->FirstChildElement("PINCODE");
442     if (pin_code) pin1_status_.pin_ = pin_code->GetText();
443 
444     auto puk_code = pin_profile->FirstChildElement("PUKCODE");
445     if (puk_code) pin1_status_.puk_ = puk_code->GetText();
446 
447     auto pin_remaining_times = pin_profile->FirstChildElement("PINREMAINTIMES");
448     if (pin_remaining_times) {
449       pin1_status_.pin_remaining_times_ = std::stoi(pin_remaining_times->GetText());
450     }
451 
452     auto puk_remaining_times = pin_profile->FirstChildElement("PUKREMAINTIMES");
453     if (puk_remaining_times) {
454       pin1_status_.puk_remaining_times_ = std::stoi(puk_remaining_times->GetText());
455     }
456 
457     // Pin2 status
458     auto pin2_code = pin_profile->FirstChildElement("PIN2CODE");
459     if (pin2_code) pin2_status_.pin_ = pin2_code->GetText();
460 
461     auto puk2_code = pin_profile->FirstChildElement("PUK2CODE");
462     if (puk2_code) pin2_status_.puk_ = puk2_code->GetText();
463 
464     auto pin2_remaining_times = pin_profile->FirstChildElement("PIN2REMAINTIMES");
465     if (pin2_remaining_times) {
466       pin2_status_.pin_remaining_times_ = std::stoi(pin2_remaining_times->GetText());
467     }
468 
469     auto puk2_remaining_times = pin_profile->FirstChildElement("PUK2REMAINTIMES");
470     if (puk2_remaining_times) {
471       pin2_status_.puk_remaining_times_ = std::stoi(puk2_remaining_times->GetText());
472     }
473   }
474 }
475 
InitializeFacilityLock()476 void SimService::InitializeFacilityLock() {
477   /* Default disable */
478   facility_lock_ = {
479       {"SC", FacilityLock(FacilityLock::LockStatus::DISABLE)},
480       {"FD", FacilityLock(FacilityLock::LockStatus::DISABLE)},
481       {"AO", FacilityLock(FacilityLock::LockStatus::DISABLE)},
482       {"OI", FacilityLock(FacilityLock::LockStatus::DISABLE)},
483       {"OX", FacilityLock(FacilityLock::LockStatus::DISABLE)},
484       {"AI", FacilityLock(FacilityLock::LockStatus::DISABLE)},
485       {"IR", FacilityLock(FacilityLock::LockStatus::DISABLE)},
486       {"AB", FacilityLock(FacilityLock::LockStatus::DISABLE)},
487       {"AG", FacilityLock(FacilityLock::LockStatus::DISABLE)},
488       {"AC", FacilityLock(FacilityLock::LockStatus::DISABLE)},
489   };
490 
491   XMLElement *root = sim_file_system_.GetRootElement();
492   if (!root) {
493     LOG(ERROR) << "Unable to find root element: IccProfile";
494     sim_status_ = SIM_STATUS_ABSENT;
495     return;
496   }
497 
498   XMLElement *facility_lock = root->FirstChildElement("FacilityLock");
499   if (!facility_lock) {
500     LOG(ERROR) << "Unable to find element: FacilityLock";
501     return;
502   }
503 
504   for (auto iter = facility_lock_.begin(); iter != facility_lock_.end(); ++iter) {
505     auto lock_status = facility_lock->FirstChildElement(iter->first.c_str());
506     if (lock_status) {
507       std::string state = lock_status->GetText();
508       if (state == "ENABLE") {
509         iter->second.lock_status = FacilityLock::LockStatus::ENABLE;
510       }
511     }
512   }
513 }
514 
SavePinStateToIccProfile()515 void SimService::SavePinStateToIccProfile() {
516   XMLElement *root = sim_file_system_.GetRootElement();
517   if (!root) {
518     LOG(ERROR) << "Unable to find root element: IccProfile";
519     sim_status_ = SIM_STATUS_ABSENT;
520     return;
521   }
522 
523   XMLElement *pin_profile = root->FirstChildElement("PinProfile");
524   if (!pin_profile) {
525     pin_profile = sim_file_system_.AppendNewElement(root, "PinProfile");
526   }
527 
528   const char* text = "PINSTATE_UNKNOWN";
529 
530   if (sim_status_ == SIM_STATUS_PUK) {
531     text = "PINSTATE_ENABLED_BLOCKED";
532   } else {
533     auto iter = facility_lock_.find("SC");
534     if (iter != facility_lock_.end()) {
535       if (iter->second.lock_status == FacilityLock::ENABLE) {
536         text = "PINSTATE_ENABLED_NOT_VERIFIED";
537       }
538     }
539   }
540 
541   // Pin1 status
542   auto pin_state = pin_profile->FirstChildElement("PINSTATE");
543   if (!pin_state) {
544     pin_state = sim_file_system_.AppendNewElementWithText(pin_profile, "PINSTATE", text);
545   } else {
546     pin_state->SetText(text);
547   }
548 
549   auto pin_code = pin_profile->FirstChildElement("PINCODE");
550   if (!pin_code) {
551     pin_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PINCODE",
552         pin1_status_.pin_.c_str());
553   } else {
554     pin_code->SetText(pin1_status_.pin_.c_str());
555   }
556 
557   auto puk_code = pin_profile->FirstChildElement("PUKCODE");
558   if (!puk_code) {
559     puk_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PUKCODE",
560         pin1_status_.puk_.c_str());
561   } else {
562     puk_code->SetText(pin1_status_.puk_.c_str());
563   }
564 
565   std::stringstream ss;
566   ss << pin1_status_.pin_remaining_times_;
567 
568   auto pin_remaining_times = pin_profile->FirstChildElement("PINREMAINTIMES");
569   if (!pin_remaining_times) {
570     pin_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
571         "PINREMAINTIMES", ss.str().c_str());
572   } else {
573     pin_remaining_times->SetText(ss.str().c_str());
574   }
575   ss.clear();
576   ss.str("");
577   ss << pin1_status_.puk_remaining_times_;
578 
579   auto puk_remaining_times = pin_profile->FirstChildElement("PUKREMAINTIMES");
580   if (!puk_remaining_times) {
581     puk_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
582         "PUKREMAINTIMES", ss.str().c_str());
583   } else {
584     puk_remaining_times->SetText(ss.str().c_str());
585   }
586 
587   // Pin2 status
588   auto pin2_code = pin_profile->FirstChildElement("PIN2CODE");
589   if (!pin2_code) {
590     pin2_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PIN2CODE",
591         pin2_status_.pin_.c_str());
592   } else {
593     pin2_code->SetText(pin2_status_.pin_.c_str());
594   }
595 
596   auto puk2_code = pin_profile->FirstChildElement("PUK2CODE");
597   if (!puk2_code) {
598     puk2_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PUK2CODE",
599         pin2_status_.puk_.c_str());
600   } else {
601     puk2_code->SetText(pin2_status_.puk_.c_str());
602   }
603 
604   ss.clear();
605   ss.str("");
606   ss << pin2_status_.pin_remaining_times_;
607 
608   auto pin2_remaining_times = pin_profile->FirstChildElement("PIN2REMAINTIMES");
609   if (!pin2_remaining_times) {
610     pin2_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
611         "PINREMAINTIMES", ss.str().c_str());
612   } else {
613     pin2_remaining_times->SetText(ss.str().c_str());
614   }
615   ss.clear();
616   ss.str("");
617   ss << pin2_status_.puk_remaining_times_;
618 
619   auto puk2_remaining_times = pin_profile->FirstChildElement("PUK2REMAINTIMES");
620   if (!puk2_remaining_times) {
621     puk2_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
622         "PUK2REMAINTIMES", ss.str().c_str());
623   } else {
624     puk2_remaining_times->SetText(ss.str().c_str());
625   }
626 
627   // Save file
628   sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
629 }
630 
SaveFacilityLockToIccProfile()631 void SimService::SaveFacilityLockToIccProfile() {
632   XMLElement *root = sim_file_system_.GetRootElement();
633   if (!root) {
634     LOG(ERROR) << "Unable to find root element: IccProfile";
635     sim_status_ = SIM_STATUS_ABSENT;
636     return;
637   }
638 
639   XMLElement *facility_lock = root->FirstChildElement("FacilityLock");
640   if (!facility_lock) {
641     facility_lock = sim_file_system_.AppendNewElement(root, "FacilityLock");
642   }
643 
644   const char* text = "DISABLE";
645 
646   for (auto iter = facility_lock_.begin(); iter != facility_lock_.end(); ++iter) {
647     if (iter->second.lock_status == FacilityLock::LockStatus::ENABLE) {
648       text = "ENABLE";
649     } else {
650       text = "DISABLE";
651     }
652     auto element = facility_lock->FirstChildElement(iter->first.c_str());
653     if (!element) {
654       element = sim_file_system_.AppendNewElementWithText(facility_lock,
655           iter->first.c_str(), text);
656     } else {
657       element->SetText(text);
658     }
659   }
660 
661   sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
662 
663   InitializeSimFileSystemAndSimState();
664   InitializeFacilityLock();
665 }
666 
IsFDNEnabled()667 bool SimService::IsFDNEnabled() {
668   auto iter = facility_lock_.find("FD");
669   if (iter != facility_lock_.end() &&
670       iter->second.lock_status == FacilityLock::LockStatus::ENABLE) {
671     return true;
672   }
673   return false;
674 }
675 
IsFixedDialNumber(std::string_view number)676 bool SimService::IsFixedDialNumber(std::string_view number) {
677   XMLElement *root = sim_file_system_.GetRootElement();
678   if (!root) return false;
679 
680   auto path = SimFileSystem::GetUsimEFPath(SimFileSystem::EFId::EF_FDN);
681 
682   size_t pos = 0;
683   auto parent = root;
684   while (pos < path.length()) {
685     std::string sub_path(path.substr(pos, 4));
686     auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
687     if (!app) return false;
688     pos += 4;
689     parent = app;
690   }
691 
692   XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", "6F3B");
693   if (!ef) return false;
694 
695   XMLElement *final = ef->FirstChildElement("SIMIO");
696   while (final) {
697     std::string record = final->GetText();
698     int footerOffset = record.length() - kFooterSizeBytes * 2;
699     int numberLength = (record[footerOffset] - '0') * 16 +
700                         record[footerOffset + 1] - '0';
701     if (numberLength > kMaxNumberSizeBytes) {  // Invalid number length
702       final = final->NextSiblingElement("SIMIO");
703       continue;
704     }
705 
706     std::string bcd_fdn = "";
707     if (numberLength * 2 == 16) {  // Skip Type(91) and Country Code(68)
708       bcd_fdn = record.substr(footerOffset + 6, numberLength * 2 - 4);
709     } else {  // Skip Type(81)
710       bcd_fdn = record.substr(footerOffset + 4, numberLength * 2 - 2);
711     }
712 
713     std::string fdn = PDUParser::BCDToString(bcd_fdn);
714     if (fdn == number) {
715       return true;
716     }
717     final = final->NextSiblingElement("SIMIO");
718   }
719 
720   return false;
721 }
722 
GetIccProfile()723 XMLElement* SimService::GetIccProfile() {
724   return sim_file_system_.GetRootElement();
725 }
726 
GetPhoneNumber()727 std::string SimService::GetPhoneNumber() {
728   XMLElement *root = sim_file_system_.GetRootElement();
729   if (!root) return "";
730 
731   auto path = SimFileSystem::GetUsimEFPath(SimFileSystem::EFId::EF_MSISDN);
732 
733   size_t pos = 0;
734   auto parent = root;
735   while (pos < path.length()) {
736     std::string sub_path(path.substr(pos, 4));
737     auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
738     if (!app) return "";
739     pos += 4;
740     parent = app;
741   }
742 
743   XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", "6F40");
744   if (!ef) return "";
745 
746   XMLElement *final = SimFileSystem::FindAttribute(ef, "cmd", "B2");;
747   if (!final) return "";
748 
749   std::string record = final->GetText();
750   int footerOffset = record.length() - kFooterSizeBytes * 2;
751   int numberLength = (record[footerOffset] - '0') * 16 +
752                       record[footerOffset + 1] - '0';
753   if (numberLength > kMaxNumberSizeBytes) {  // Invalid number length
754     return "";
755   }
756 
757   std::string bcd_number = "";
758   if (numberLength * 2 == 16) {  // Skip Type(91) and Country Code(68)
759     bcd_number = record.substr(footerOffset + 6, numberLength * 2 - 4);
760   } else {  // Skip Type(81)
761     bcd_number = record.substr(footerOffset + 4, numberLength * 2 - 2);
762   }
763 
764   return PDUParser::BCDToString(bcd_number);
765 }
766 
GetSimStatus() const767 SimService::SimStatus SimService::GetSimStatus() const {
768   return sim_status_;
769 }
770 
GetSimOperator()771 std::string SimService::GetSimOperator() {
772   XMLElement *root = sim_file_system_.GetRootElement();
773   if (!root) return "";
774 
775   XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
776   if (!mf) return "";
777 
778   XMLElement* df = SimFileSystem::FindAttribute(mf, "path", DF_ADF);
779   if (!df) return "";
780 
781   XMLElement* ef = SimFileSystem::FindAttribute(df, "id", "6F07");
782   if (!ef) return "";
783 
784   XMLElement *cimi = ef->FirstChildElement("CIMI");
785   if (!cimi) return "";
786   std::string imsi = cimi->GetText();
787 
788   ef = SimFileSystem::FindAttribute(df, "id", "6FAD");
789   if (!ef) return "";
790 
791   XMLElement *sim_io = ef->FirstChildElement("SIMIO");
792   while (sim_io) {
793     const XMLAttribute *attr_cmd = sim_io->FindAttribute("cmd");
794     std::string attr_value = attr_cmd ? attr_cmd->Value() : "";
795     if (attr_cmd && attr_value == "B0") {
796       break;
797     }
798 
799     sim_io = sim_io->NextSiblingElement("SIMIO");
800   }
801 
802   if (!sim_io) return "";
803 
804   std::string length = sim_io->GetText();
805   int mnc_size = std::stoi(length.substr(length.size() -2));
806 
807   return imsi.substr(0, 3 + mnc_size);
808 }
809 
SetupDependency(NetworkService * net)810 void SimService::SetupDependency(NetworkService* net) {
811   network_service_ = net;
812 }
813 
814 /**
815  * AT+CPIN
816  *   Set command sends to the MT a password which is necessary before it can be
817  * operated.
818  *   Read command returns an alphanumeric string indicating whether some
819  * password is required or not.
820  *
821  * Command                            Possible response(s)
822  * +CPIN=<pin>[,<newpin>]              +CME ERROR: <err>
823  * +CPIN?                              +CPIN: <code>
824  *                                     +CME ERROR: <err>
825  * <pin>, <newpin>: string type values.
826  * <code> values reserved by the present document:
827  *    READY   MT is not pending for any password
828  *   SIM PIN  MT is waiting SIM PIN to be given
829  *   SIM PUK  MT is waiting SIM PUK to be given
830  *
831  * see RIL_REQUEST_GET_SIM_STATUS in RIL
832  */
HandleSIMStatusReq(const Client & client)833 void SimService::HandleSIMStatusReq(const Client& client) {
834   std::vector<std::string> responses;
835   auto iter = gSimStatusResponse.find(sim_status_);
836   if (iter != gSimStatusResponse.end()) {
837     responses.push_back(iter->second);
838     responses.push_back("OK");
839   } else {
840     sim_status_ = SIM_STATUS_ABSENT;
841     responses.push_back(kCmeErrorSimNotInserted);
842   }
843   client.SendCommandResponse(responses);
844 }
845 
846 /**
847  * AT+CRSM
848  *   By using this command instead of Generic SIM Access +CSIM TE application
849  *   has easier but more limited access to the SIM database.
850  *
851  *   Command                                Possible response(s)
852  * +CRSM=<command>[,<fileid>                +CRSM: <sw1>,<sw2>[,<response>]
853  * [,<P1>,<P2>,<P3>[,<data>[,<pathid>]]]]   +CME ERROR: <err>
854  *
855  * <command>: (command passed on by the MT to the SIM; refer 3GPP TS 51.011 [28]):
856  *   176 READ BINARY
857  *   178 READ RECORD
858  *   192 GET RESPONSE
859  *   214 UPDATE BINARY
860  *   220 UPDATE RECORD
861  *   242 STATUS
862  *   203 RETRIEVE DATA
863  *   219 SET DATA
864  *
865  * <fileid>: integer type; this is the identifier of a elementary datafile on SIM.
866  *           Mandatory for every command except STATUS.
867  *
868  * <P1>, <P2>, <P3>: integer type; parameters passed on by the MT to the SIM.
869  *                   These parameters are mandatory for every command,
870  *                   except GET RESPONSE and STATUS.
871  *
872  * <data>: information which shall be written to the SIM (hexadecimal character format).
873  *
874  * <pathid>: string type; contains the path of an elementary file on the SIM/UICC
875  *           in hexadecimal format.
876  *
877  * <sw1>, <sw2>: integer type; information from the SIM about the execution of
878  *               the actual command.
879  *
880  * <response>: response of a successful completion of the command previously issued
881  *             (hexadecimal character format; refer +CSCS).
882  */
HandleSIM_IO(const Client & client,const std::string & command)883 void SimService::HandleSIM_IO(const Client& client,
884                               const std::string& command) {
885   std::vector<std::string> kFileNotFoud = {"+CRSM: 106,130", "OK"};
886   std::vector<std::string> responses;
887 
888   CommandParser cmd(command);
889   cmd.SkipPrefix();  // skip "AT+CRSM="
890 
891   if (*cmd == "242,0,0,0,0") { //  for cts teset
892     responses.push_back("+CRSM: 144,0,62338202782183023F00A50C80016187010183040007DBF08A01058B062F0601020002C60C90016083010183010A83010D8102FFFF");
893     responses.push_back("OK");
894     client.SendCommandResponse(responses);
895     return;
896   }
897 
898   auto c = cmd.GetNextStrDeciToHex();
899   auto id = cmd.GetNextStrDeciToHex();
900   auto p1 = cmd.GetNextStrDeciToHex();
901   auto p2 = cmd.GetNextStrDeciToHex();
902   auto p3 = cmd.GetNextStrDeciToHex();
903 
904   auto data = cmd.GetNextStr(',');
905   std::string path(cmd.GetNextStr());
906 
907   XMLElement *root = sim_file_system_.GetRootElement();
908   if (!root) {
909     LOG(ERROR) << "Unable to find root element: IccProfile";
910     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
911     return;
912   }
913 
914   SimFileSystem::EFId fileid = (SimFileSystem::EFId)std::stoi(id, nullptr, 16);
915   if (path == "") {
916     path = SimFileSystem::GetUsimEFPath(fileid);
917   }
918   // EF_ADN under DF_PHONEBOOK is mapped to EF_ADN under DF_TELECOM per
919   // 3GPP TS 31.102 4.4.2
920   if (fileid == SimFileSystem::EF_ADN &&
921       path == SimFileSystem::GetUsimEFPath(fileid)) {
922     id = "4F3A";
923     path = MF_SIM + DF_TELECOM + DF_PHONEBOOK;
924   }
925 
926   size_t pos = 0;
927   auto parent = root;
928   while (pos < path.length()) {
929     std::string sub_path(path.substr(pos, 4));
930     auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
931     if (!app) {
932       client.SendCommandResponse(kFileNotFoud);
933       return;
934     }
935     pos += 4;
936     parent = app;
937   }
938 
939   XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", id);
940   if (!ef) {
941     client.SendCommandResponse(kFileNotFoud);
942     return;
943   }
944 
945   XMLElement *final = ef->FirstChildElement("SIMIO");
946   while (final) {
947     const XMLAttribute *attr_cmd = final->FindAttribute("cmd");
948     const XMLAttribute *attr_p1 = final->FindAttribute("p1");
949     const XMLAttribute *attr_p2 = final->FindAttribute("p2");
950     const XMLAttribute *attr_p3 = final->FindAttribute("p3");
951     const XMLAttribute *attr_data = final->FindAttribute("data");
952 
953     if (c != "DC" && c != "D6") {  // Except UPDATE RECORD or UPDATE BINARY
954       if ((attr_cmd && attr_cmd->Value() != c) ||
955           (attr_data && attr_data->Value() != data)) {
956         final = final->NextSiblingElement("SIMIO");
957         continue;
958       }
959     }
960     if (attr_p1 && attr_p1->Value() == p1 &&
961         attr_p2 && attr_p2->Value() == p2 &&
962         attr_p3 && attr_p3->Value() == p3) {
963       break;
964     }
965     final = final->NextSiblingElement("SIMIO");
966   }
967 
968   if (!final) {
969     client.SendCommandResponse(kFileNotFoud);
970     return;
971   }
972 
973   std::string response = "+CRSM: ";
974   if (c == "DC" || c == "D6") {
975     std::string temp = "144,0,";
976     temp += data;
977     final->SetText(temp.c_str());
978     sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
979     response.append("144,0");
980   } else {
981     response.append(final->GetText());
982   }
983 
984   responses.push_back(response);
985   responses.push_back("OK");
986   client.SendCommandResponse(responses);
987 }
988 
OnSimStatusChanged()989 void SimService::OnSimStatusChanged() {
990   auto ptr = network_service_;
991   if (ptr) {
992     ptr->OnSimStatusChanged(sim_status_);
993   }
994 }
995 
checkPin1AndAdjustSimStatus(std::string_view pin)996 bool SimService::checkPin1AndAdjustSimStatus(std::string_view pin) {
997   if (pin1_status_.VerifyPIN(pin) == true) {
998     sim_status_ = SIM_STATUS_READY;
999     OnSimStatusChanged();
1000     return true;
1001   }
1002 
1003   if (pin1_status_.pin_remaining_times_ <= 0) {
1004     sim_status_ = SIM_STATUS_PUK;
1005     OnSimStatusChanged();
1006   }
1007 
1008   return false;
1009 }
1010 
1011 /* AT+CSIM */
HandleCSIM_IO(const Client & client,const std::string & command)1012 void SimService::HandleCSIM_IO(const Client& client,
1013                               const std::string& command) {
1014   std::vector<std::string> responses;
1015 
1016   CommandParser cmd(command);
1017   cmd.SkipPrefix();  // skip "AT+CSIM="
1018 
1019   cmd.SkipComma();
1020   auto data = cmd.GetNextStr();
1021 
1022   XMLElement *root = sim_file_system_.GetRootElement();
1023   if (!root) {
1024     LOG(ERROR) << "Unable to find root element: IccProfile";
1025     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1026     return;
1027   }
1028   // Get aid
1029   XMLElement* df = SimFileSystem::FindAttribute(root, "aid", "CSIM");
1030   if (!df) {
1031     client.SendCommandResponse(kCmeErrorNotFound);
1032     return;
1033   }
1034 
1035   std::string data_value(data);
1036   if (data_value.length() > 10) {  // for open channel with csim
1037       responses.push_back("+CSIM: 4,9000");
1038       responses.push_back("OK");
1039       client.SendCommandResponse(responses);
1040       return;
1041   }
1042   XMLElement* final = SimFileSystem::FindAttribute(df, "cmd", data_value);
1043   if (!final) {
1044     client.SendCommandResponse(kCmeErrorNotFound);
1045     return;
1046   }
1047 
1048   auto id = data_value.substr(data_value.length() - 2, 2);
1049 
1050   std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
1051   for (; iter != logical_channels_.end(); ++iter) {
1052     if (!iter->is_open) break;
1053   }
1054 
1055   if (iter != logical_channels_.end() && iter->session_id ==stoi(id)) {
1056     iter->is_open = true;
1057     iter->df_name = "CSIM";
1058   }
1059 
1060   std::stringstream ss;
1061   ss << "+CSIM: " << final->GetText();
1062 
1063   responses.push_back(ss.str());
1064   responses.push_back("OK");
1065   client.SendCommandResponse(responses);
1066 }
1067 
ChangePin1AndAdjustSimStatus(PinStatus::ChangeMode mode,std::string_view pin,std::string_view new_pin)1068 bool SimService::ChangePin1AndAdjustSimStatus(PinStatus::ChangeMode mode,
1069                                               std::string_view pin,
1070                                               std::string_view new_pin) {
1071   if (pin1_status_.ChangePIN(mode, pin, new_pin) == true) {
1072     sim_status_ = SIM_STATUS_READY;
1073     OnSimStatusChanged();
1074     return true;
1075   }
1076   if (sim_status_ == SIM_STATUS_READY && pin1_status_.pin_remaining_times_ <= 0) {
1077     sim_status_ = SIM_STATUS_PIN;
1078     OnSimStatusChanged();
1079   } else if (sim_status_ == SIM_STATUS_PIN && pin1_status_.puk_remaining_times_ <= 0) {
1080     sim_status_ = SIM_STATUS_ABSENT;
1081     OnSimStatusChanged();
1082   }
1083   return false;
1084 }
1085 
HandleChangeOrEnterPIN(const Client & client,const std::string & command)1086 void SimService::HandleChangeOrEnterPIN(const Client& client,
1087                                         const std::string& command) {
1088   std::vector<std::string> responses;
1089 
1090   CommandParser cmd(command);
1091   cmd.SkipPrefix();  // skip "AT+CPIN="
1092   switch (sim_status_) {
1093     case SIM_STATUS_ABSENT:
1094       responses.push_back(kCmeErrorSimNotInserted);
1095       break;
1096     case SIM_STATUS_NOT_READY:
1097       responses.push_back(kCmeErrorSimBusy);
1098       break;
1099     case SIM_STATUS_READY: {
1100       /*
1101        * this may be a request to change the PIN with pin and new pin:
1102        *    AT+CPIN=pin,newpin
1103        * or a request to enter the PIN2
1104        *    AT+CPIN=pin2
1105        */
1106       auto pos = cmd->find(',');
1107       if (pos != std::string_view::npos) {  // change pin with new pin
1108         auto pin = cmd.GetNextStr(',');
1109         auto new_pin = *cmd;
1110 
1111         if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PIN, pin, new_pin)) {
1112           responses.push_back("OK");
1113         } else {
1114           responses.push_back(kCmeErrorIncorrectPassword);  /* incorrect PIN */
1115         }
1116       } else {  // verify pin2
1117         if (pin2_status_.VerifyPIN(*cmd) == true) {
1118           responses.push_back("OK");
1119         } else {
1120           responses.push_back(kCmeErrorIncorrectPassword);  /* incorrect PIN2 */
1121         }
1122       }
1123       break;
1124     }
1125     case SIM_STATUS_PIN: {  /* waiting for PIN */
1126       if (checkPin1AndAdjustSimStatus(*cmd) == true) {
1127         responses.push_back("OK");
1128       } else {
1129         responses.push_back(kCmeErrorIncorrectPassword);
1130       }
1131       break;
1132     }
1133     case SIM_STATUS_PUK: {
1134       /*
1135        * this may be a request to unlock the puk with new pin:
1136        *    AT+CPIN=puk,newpin
1137        */
1138       auto pos = cmd->find(',');
1139       if (pos != std::string_view::npos) {
1140         auto puk = cmd.GetNextStr(',');
1141         auto new_pin = *cmd;
1142         if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PUK, puk, new_pin)) {
1143           responses.push_back("OK");
1144         } else {
1145           responses.push_back(kCmeErrorIncorrectPassword);
1146         }
1147       } else {
1148         responses.push_back(kCmeErrorOperationNotAllowed);
1149       }
1150       break;
1151     }
1152     default:
1153       responses.push_back(kCmeErrorOperationNotAllowed);
1154       break;
1155   }
1156 
1157   client.SendCommandResponse(responses);
1158 }
1159 
1160 /**
1161  * AT+CIMI
1162  *   Execution command causes the TA to return <IMSI>, which is intended to
1163  * permit the TE to identify the individual SIM card or active application in
1164  * the UICC (GSM or USIM) which is attached to MT.
1165  *
1166  * Command                            Possible response(s)
1167  * +CIMI                               <IMSI>
1168  *                                     +CME ERROR: <err>
1169  *
1170  * <IMSI>: International Mobile Subscriber Identity (string without double quotes)
1171  *
1172  * see RIL_REQUEST_GET_IMSI in RIL
1173  */
HandleGetIMSI(const Client & client)1174 void SimService::HandleGetIMSI(const Client& client) {
1175   std::vector<std::string> responses;
1176 
1177   XMLElement *root = sim_file_system_.GetRootElement();
1178   if (!root) {
1179     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1180     return;
1181   }
1182 
1183   XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
1184   if (!mf) {
1185     client.SendCommandResponse(kCmeErrorNotFound);
1186     return;
1187   }
1188 
1189   XMLElement* df = SimFileSystem::FindAttribute(mf, "path", DF_ADF);
1190   if (!df) {
1191     client.SendCommandResponse(kCmeErrorNotFound);
1192     return;
1193   }
1194 
1195   XMLElement* ef = SimFileSystem::FindAttribute(df, "id", "6F07");
1196   if (!ef) {
1197     client.SendCommandResponse(kCmeErrorNotFound);
1198     return;
1199   }
1200 
1201   XMLElement *final = ef->FirstChildElement("CIMI");
1202   if (!final) {
1203     client.SendCommandResponse(kCmeErrorNotFound);
1204     return;
1205   }
1206 
1207   responses.push_back(final->GetText());
1208   responses.push_back("OK");
1209   client.SendCommandResponse(responses);
1210 }
1211 
1212 /**
1213  * AT+CICCID
1214  *   Integrated Circuit Card IDentifier (ICCID) is Unique Identifier of the SIM CARD.
1215  *  File is located in the SIM card at EFiccid (0x2FE2).
1216  *
1217  * see RIL_REQUEST_GET_SIM_STATUS in RIL
1218  */
HandleGetIccId(const Client & client)1219 void SimService::HandleGetIccId(const Client& client) {
1220   std::vector<std::string> responses;
1221 
1222   XMLElement *root = sim_file_system_.GetRootElement();
1223   if (!root) {
1224     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1225     return;
1226   }
1227 
1228   XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
1229   if (!mf) {
1230     client.SendCommandResponse(kCmeErrorNotFound);
1231     return;
1232   }
1233 
1234   XMLElement* ef = SimFileSystem::FindAttribute(mf, "id", "2FE2");
1235   if (!ef) {
1236     client.SendCommandResponse(kCmeErrorNotFound);
1237     return;
1238   }
1239 
1240   XMLElement *final = ef->FirstChildElement("CCID");
1241   if (!final) {
1242     client.SendCommandResponse(kCmeErrorNotFound);
1243     return;
1244   }
1245 
1246   responses.push_back(final->GetText());
1247   responses.push_back("OK");
1248   client.SendCommandResponse(responses);
1249 }
1250 
1251 /*
1252  * AT+CLCK
1253  *   Execute command is used to lock, unlock or interrogate a MT or a network
1254  * facility <fac>.
1255  *
1256  * Command                            Possible response(s)
1257  * +CLCK=<fac>, <mode> [, <password>   OK or +CME ERROR: <err>
1258  *       [, <class>]]                  +CLCK: <status>[,<class1>[<CR><LF>+CLCK:
1259  *                                     <status>,<class2>[...]](when mode=2,it’s
1260  *                                     in inquiry status.)
1261  * <fac> values reserved by the present document:
1262  *    "SC": SIM (lock SIM/UICC card installed in the currently selected card
1263  *          slot) (SIM/UICC asks password in MT power‑up and when this lock
1264  *          command issued).
1265  *    "FD": SIM card or active application in the UICC (GSM or USIM) fixed
1266  *          dialling memory feature (if PIN2 authentication has not been done
1267  *          during the current session, PIN2 is required as <passwd>).
1268  * <mode>: integer type
1269  *      0: unlock
1270  *      1: lock
1271  *      2: query status
1272  * <status>: integer type
1273  *        0: not active
1274  *        1: active
1275  * <passwd>: string type; shall be the same as password specified for the
1276  *           facility from the MT user interface or with command
1277  *           Change Password +CPWD.
1278  * <classx> is a sum of integers each representing a class of information
1279  *          (default 7 - voice, data and fax):
1280  *        1 voice (telephony)
1281  *        2 data
1282  *        4 fax (facsimile services)
1283  *        8 short message service
1284  *       16 data circuit sync
1285  *       32 data circuit async
1286  *       64 dedicated packet access
1287  *      128 dedicated PAD access
1288  *
1289  * see RIL_REQUEST_SET_FACILITY_LOCK in RIL
1290  */
HandleFacilityLock(const Client & client,const std::string & command)1291 void SimService::HandleFacilityLock(const Client& client,
1292                                     const std::string& command) {
1293   CommandParser cmd(command);
1294   std::string lock(cmd.GetNextStr());
1295   int mode = cmd.GetNextInt();
1296   auto password = cmd.GetNextStr();
1297   // Ignore class from RIL
1298 
1299   auto iter = facility_lock_.find(lock);
1300   if (iter == facility_lock_.end()) {
1301     client.SendCommandResponse(kCmeErrorOperationNotSupported);
1302     return;
1303   }
1304 
1305   std::stringstream ss;
1306   std::vector<std::string> responses;
1307   switch (mode) {
1308     case FacilityLock::Mode::QUERY: {
1309       ss << "+CLCK: " << iter->second.lock_status;
1310       responses.push_back(ss.str());
1311       responses.push_back("OK");
1312       break;
1313     }
1314     case FacilityLock::Mode::LOCK:
1315     case FacilityLock::Mode::UNLOCK: {
1316       if (lock == "SC") {
1317         if (checkPin1AndAdjustSimStatus(password) == true) {
1318           iter->second.lock_status = (FacilityLock::LockStatus)mode;
1319           responses.push_back("OK");
1320         } else {
1321           responses.push_back(kCmeErrorIncorrectPassword);
1322         }
1323       } else if (lock == "FD") {
1324         if (pin2_status_.VerifyPIN(password) == true) {
1325           iter->second.lock_status = (FacilityLock::LockStatus)mode;
1326           responses.push_back("OK");
1327         } else {
1328           responses.push_back(kCmeErrorIncorrectPassword);
1329         }
1330       } else {  // Don't need password except 'SC' and 'FD'
1331         iter->second.lock_status = (FacilityLock::LockStatus)mode;
1332         responses.push_back("OK");
1333       }
1334       break;
1335     }
1336     default:
1337       responses.push_back(kCmeErrorInCorrectParameters);
1338       break;
1339   }
1340 
1341   client.SendCommandResponse(responses);
1342 }
1343 
1344 /**
1345  * AT+CCHO
1346  *   The currently selected UICC will open a new logical channel; select the
1347  * application identified by the <dfname> received with this command and return
1348  * a session Id as the response.
1349  *
1350  * Command                            Possible response(s)
1351  * +CCHO=<dfname>                      <sessionid>
1352  *                                     +CME ERROR: <err>
1353  *
1354  * <dfname>: all selectable applications in the UICC are referenced by a DF
1355  *           name coded on 1 to 16 bytes.
1356  * <sessionid>: integer type; a session Id to be used in order to target a
1357  *            specific application on the smart card (e.g. (U)SIM, WIM, ISIM)
1358  *            using logical channels mechanism.
1359  *
1360  * see RIL_REQUEST_SIM_OPEN_CHANNEL in RIL
1361  */
HandleOpenLogicalChannel(const Client & client,const std::string & command)1362 void SimService::HandleOpenLogicalChannel(const Client& client,
1363                                           const std::string& command) {
1364   std::vector<std::string> responses;
1365 
1366   CommandParser cmd(command);
1367   cmd.SkipPrefix();  // skip AT+CCHO=
1368   if (cmd->empty()) {
1369     client.SendCommandResponse(kCmeErrorInCorrectParameters);
1370     return;
1371   }
1372 
1373   XMLElement *root = sim_file_system_.GetRootElement();
1374   if (!root) {
1375     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1376     return;
1377   }
1378 
1379   std::string aid_value(*cmd);
1380   XMLElement* df = SimFileSystem::FindAttribute(root, "aid", aid_value);
1381   if (!df) {
1382     client.SendCommandResponse(kCmeErrorNotFound);
1383     return;
1384   }
1385 
1386   std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
1387   for (; iter != logical_channels_.end(); ++iter) {
1388     if (!iter->is_open) break;
1389   }
1390 
1391   if (iter != logical_channels_.end()) {
1392     iter->is_open = true;
1393     iter->df_name = *cmd;
1394 
1395     std::stringstream ss;
1396     ss << iter->session_id;
1397     responses.push_back(ss.str());
1398     responses.push_back("OK");
1399   } else {
1400     responses.push_back(kCmeErrorMemoryFull);
1401   }
1402 
1403   client.SendCommandResponse(responses);
1404 }
1405 
1406 /**
1407  * AT+CCHC
1408  *   This command asks the ME to close a communication session with the active
1409  * UICC.
1410  *
1411  * Command                            Possible response(s)
1412  * +CCHC=<sessionid>                   +CCHC
1413  *                                     +CME ERROR: <err>
1414  * <sessionid>: see AT+CCHO
1415  *
1416  * see RIL_REQUEST_SIM_CLOSE_CHANNEL in RIL
1417  */
HandleCloseLogicalChannel(const Client & client,const std::string & command)1418 void SimService::HandleCloseLogicalChannel(const Client& client,
1419                                            const std::string& command) {
1420   std::vector<std::string> responses;
1421 
1422   CommandParser cmd(command);
1423   cmd.SkipPrefix();  // skip AT+CCHC=
1424 
1425   int session_id = cmd.GetNextInt();
1426   std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
1427   for (; iter != logical_channels_.end(); ++iter) {
1428     if (iter->session_id == session_id) break;
1429   }
1430 
1431   if (iter != logical_channels_.end() && iter->is_open) {
1432     iter->is_open = false;
1433     iter->df_name.clear();
1434     responses.push_back("+CCHC");
1435     responses.push_back("OK");
1436   } else {
1437     responses.push_back(kCmeErrorNotFound);
1438   }
1439   client.SendCommandResponse(responses);
1440 }
1441 
1442 /**
1443  * AT+CGLA
1444  *   Set command transmits to the MT the <command> it then shall send as it is
1445  * to the selected UICC. In the same manner the UICC <response> shall be sent
1446  * back by the MT to the TA as it is.
1447  *
1448  * Command                            Possible response(s)
1449  * +CGLA=<sessionid>,<length>,         +CGLA: <length>,<response>
1450  *                                     +CME ERROR: <err>
1451  * <sessionid>: AT+CCHO
1452  * <length>: integer type; length of the characters that are sent to TE in
1453  *         <command> or <response> .
1454  * <command>: command passed on by the MT to the UICC in the format as described
1455  *          in 3GPP TS 31.101 [65] (hexadecimal character format; refer +CSCS).
1456  * <response>: response to the command passed on by the UICC to the MT in the
1457  *           format as described in 3GPP TS 31.101 [65] (hexadecimal character
1458  *           format; refer +CSCS).
1459  *
1460  * see RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL in RIL
1461  */
HandleTransmitLogicalChannel(const Client & client,const std::string & command)1462 void SimService::HandleTransmitLogicalChannel(const Client& client,
1463                                               const std::string& command) {
1464   std::vector<std::string> responses;
1465 
1466   CommandParser cmd(command);
1467   cmd.SkipPrefix();  // skip AT+CGLA=
1468 
1469   int session_id = cmd.GetNextInt();
1470   int length = cmd.GetNextInt();
1471   if (cmd->length() != length) {
1472     client.SendCommandResponse(kCmeErrorInCorrectParameters);
1473     return;
1474   }
1475 
1476   // Check if session id is opened
1477   auto iter = logical_channels_.begin();
1478   for (; iter != logical_channels_.end(); ++iter) {
1479     if (iter->session_id == session_id && iter->is_open) {
1480       break;
1481     }
1482   }
1483 
1484   if (iter == logical_channels_.end()) {
1485     client.SendCommandResponse(kCmeErrorInvalidIndex);
1486     return;
1487   }
1488 
1489   XMLElement *root = sim_file_system_.GetRootElement();
1490   if (!root) {
1491     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1492     return;
1493   }
1494 
1495   // Get aid
1496   XMLElement* df = SimFileSystem::FindAttribute(root, "aid", iter->df_name);
1497   if (!df) {
1498     client.SendCommandResponse(kCmeErrorNotFound);
1499     return;
1500   }
1501 
1502   if (iter->df_name != "CSIM") {
1503     std::string command_vaule(*cmd);
1504     if (command_vaule.substr(2, 2) == "a4") {
1505       last_file_id_ = command_vaule.substr(command_vaule.length() - 4, 4);
1506     }
1507       df = SimFileSystem::FindAttribute(df, "id", last_file_id_);
1508       if (!df) {
1509       client.SendCommandResponse(kCmeErrorNotFound);
1510       return;
1511     }
1512   }
1513 
1514   std::string attr_value(*cmd);
1515   XMLElement* final = SimFileSystem::FindAttribute(df, "cmd", attr_value);
1516   if (!final) {
1517     client.SendCommandResponse(kCmeErrorNotFound);
1518     return;
1519   }
1520 
1521   std::stringstream ss;
1522   ss << "+CGLA: " << final->GetText();
1523   responses.push_back(ss.str());
1524   responses.push_back("OK");
1525   client.SendCommandResponse(responses);
1526 }
1527 
1528 /**
1529  * AT+CPWD
1530  *   Action command sets a new password for the facility lock function defined
1531  * by command Facility Lock +CLCK
1532  *
1533  * Command                              Possible response(s)
1534  * +CPWD=<fac>,<oldpwd>,<newpwd>          +CME ERROR: <err>
1535  *
1536  * <fac>:
1537  *   "P2"  SIM PIN2
1538  *   refer Facility Lock +CLCK for other values
1539  * <oldpwd>, <newpwd>:
1540  *   string type; <oldpwd> shall be the same as password specified for the
1541  *   facility from the MT user interface or with command Change Password +CPWD
1542  *   and <newpwd> is the new password; maximum length of password can be determined
1543  *   with <pwdlength>
1544  * <pwdlength>: integer type maximum length of the password for the facility
1545  */
HandleChangePassword(const Client & client,const std::string & command)1546 void SimService::HandleChangePassword(const Client& client,
1547                                       const std::string& command) {
1548   std::string response = kCmeErrorIncorrectPassword;
1549 
1550   CommandParser cmd(command);
1551   cmd.SkipPrefix();
1552   auto lock = cmd.GetNextStr();
1553   auto old_password = cmd.GetNextStr();
1554   auto new_password = cmd.GetNextStr();
1555 
1556   if (lock == "SC") {
1557     if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PIN, old_password, new_password)) {
1558       response = "OK";
1559     }
1560   } else if (lock == "P2" || lock == "FD") {
1561     if (pin2_status_.ChangePIN(PinStatus::WITH_PIN, old_password, new_password)) {
1562       response = "OK";
1563     }
1564   } else {
1565     response = kCmeErrorOperationNotSupported;;
1566   }
1567 
1568   client.SendCommandResponse(response);
1569 }
1570 
1571 /**
1572  * AT+CPINR
1573  *   Execution command cause the MT to return the number of remaining PIN retries
1574  * for the MT passwords with intermediate result code
1575  *
1576  * Command                        Possible response(s)
1577  * +CPINR[=<sel_code>]            +CPINR: <code>,<retries>[,<default_retries>]
1578  *
1579  * <retries>:
1580  *   integer type. Number of remaining retries per PIN.
1581  * <default_retries>:
1582  *   integer type. Number of default/initial retries per PIN.
1583  * <code>:
1584  *   Type of PIN. All values listed under the description of the AT+CPIN command
1585  * <sel_code>: String type. Same values as for the <code> and <ext_code> parameters.
1586  *   these values are strings and shall be indicated within double quotes.
1587  */
HandleQueryRemainTimes(const Client & client,const std::string & command)1588 void SimService::HandleQueryRemainTimes(const Client& client,
1589                                         const std::string& command) {
1590   std::vector<std::string> responses;
1591   std::stringstream ss;
1592 
1593   CommandParser cmd(command);
1594   cmd.SkipPrefix();
1595   auto lock_type = cmd.GetNextStr();
1596 
1597   if (lock_type == "SIM PIN") {
1598     ss << "+CPINR: SIM PIN," << pin1_status_.pin_remaining_times_ << ","
1599                              << kSimPinMaxRetryTimes;
1600   } else if (lock_type == "SIM PUK") {
1601     ss << "+CPINR: SIM PUK," << pin1_status_.puk_remaining_times_ << ","
1602                              << kSimPukMaxRetryTimes;
1603   } else if (lock_type == "SIM PIN2") {
1604     ss << "+CPINR: SIM PIN2," << pin2_status_.pin_remaining_times_ << ","
1605                               << kSimPinMaxRetryTimes;
1606   } else if (lock_type == "SIM PUK2") {
1607     ss << "+CPINR: SIM PUK2," << pin2_status_.puk_remaining_times_ << ","
1608             << kSimPukMaxRetryTimes;
1609   } else {
1610     responses.push_back(kCmeErrorInCorrectParameters);
1611     client.SendCommandResponse(responses);
1612     return;
1613   }
1614 
1615   responses.push_back(ss.str());
1616   responses.push_back("OK");
1617   client.SendCommandResponse(responses);
1618 }
1619 
1620 /**
1621  * see
1622  *   RIL_REQUEST_CDMA_SET_SUBSCRIPTION or
1623  *   RIL_REQUEST_CDMA_GET_SUBSCRIPTION in RIL
1624  */
HandleCdmaSubscriptionSource(const Client & client,const std::string & command)1625 void SimService::HandleCdmaSubscriptionSource(const Client& client,
1626                                               const std::string& command) {
1627   std::vector<std::string> responses;
1628 
1629   CommandParser cmd(command);
1630   if (*cmd == "AT+CCSS?") {  // Query
1631     std::stringstream ss;
1632     ss << "+CCSS: " << cdma_subscription_source_;
1633     responses.push_back(ss.str());
1634   } else { // Set
1635     cdma_subscription_source_ = cmd.GetNextInt();
1636   }
1637   responses.push_back("OK");
1638   client.SendCommandResponse(responses);
1639 }
1640 
1641 /**
1642  * see
1643  *   RIL_REQUEST_CDMA_SET_ROAMNING_PREFERENCE or
1644  *   RIL_REQUEST_CDMA_GET_ROAMNING_PREFERENCE in RIL
1645  */
HandleCdmaRoamingPreference(const Client & client,const std::string & command)1646 void SimService::HandleCdmaRoamingPreference(const Client& client,
1647                                              const std::string& command) {
1648   std::vector<std::string> responses;
1649 
1650   CommandParser cmd(command);
1651   if (*cmd == "AT+WRMP?") {  // Query
1652     std::stringstream ss;
1653     ss << "+WRMP: " << cdma_roaming_preference_;
1654     responses.push_back(ss.str());
1655   } else { // Set
1656     cdma_roaming_preference_ = cmd.GetNextInt();
1657   }
1658   responses.push_back("OK");
1659   client.SendCommandResponse(responses);
1660 }
1661 
HandleSimAuthentication(const Client & client,const std::string & command)1662 void SimService::HandleSimAuthentication(const Client& client,
1663                                              const std::string& command) {
1664   std::vector<std::string> responses;
1665 
1666   CommandParser cmd(command);
1667   cmd.SkipPrefix();
1668 
1669   // Input format: ^MBAU=<RAND>[,<AUTN>]
1670   auto cmds = cmd.GetNextStr();
1671   // Output format: ^MBAU: <STATUS>[,<KC>,<SRES>][,<CK>,<IK>,<RES/AUTS>]
1672   std::stringstream ss;
1673 
1674   // Authentication challenges done in CTS.
1675   if (cmds == "2713AB0BA8E8E7D8F1D74545BA03F563") {
1676     // CarrierApiTest#testGetIccAuthentication (base64Challenge)
1677     ss << "^MBAU: 0,8F2980FC3872FF89,E9620240";
1678   } else if (cmds == "C3718EC16B3C2A66F8A7200A64069F04") {
1679     // CarrierApiTest#testGetIccAuthentication (base64Challenge2)
1680     ss << "^MBAU: 0,CFDA6C980502DA48,F7E53577";
1681   } else if (cmds == "11111111111111111111111111111111") {
1682     // CarrierApiTest#testEapSimAuthentication
1683     ss << "^MBAU: 0,0000000000000000,00000000";
1684   } else if (cmds == "11111111111111111111111111111111,12351417161900001130131215141716") {
1685     // CarrierApiTest#testEapAkaAuthentication
1686     // Note: the "DB" prefix gets appended where the RIL parses this response.
1687     ss << "^MBAU: 0,111013121514171619181B1A1D1C1F1E,1013121514171619181B1A1D1C1F1E11,"
1688           "13121514171619181B1A1D1C1F1E1110";
1689   }
1690 
1691   responses.push_back(ss.str());
1692   responses.push_back("OK");
1693   client.SendCommandResponse(responses);
1694 }
1695 
1696 
1697 }  // namespace cuttlefish
1698