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       CommandHandler("+REMOTEUPADATEPHONENUMBER",
353                     [this](const Client& client, std::string& cmd) {
354                       this->HandlePhoneNumberUpdate(client,cmd);
355                     }),
356   };
357   return (command_handlers);
358 }
359 
InitializeServiceState()360 void SimService::InitializeServiceState() {
361   InitializeSimFileSystemAndSimState();
362 
363   InitializeFacilityLock();
364 
365   // Max logical channels: 3
366   logical_channels_ = {
367       LogicalChannel(1), LogicalChannel(2), LogicalChannel(kMaxLogicalChannels),
368   };
369 }
370 
InitializeSimFileSystemAndSimState()371 void SimService::InitializeSimFileSystemAndSimState() {
372   auto nvram_config = NvramConfig::Get();
373   auto sim_type = nvram_config->sim_type();
374   std::stringstream ss;
375   if (sim_type == 2) {  // Special sim card for CtsCarrierApiTestCases
376     ss << "iccprofile_for_sim" << service_id_ << "_for_CtsCarrierApiTestCases.xml";
377   } else {
378     ss << "iccprofile_for_sim" << service_id_ << ".xml";
379   }
380   auto icc_profile_name = ss.str();
381 
382   auto icc_profile_path = cuttlefish::modem::DeviceConfig::PerInstancePath(
383       icc_profile_name.c_str());
384   std::string file = icc_profile_path;
385 
386   if (!cuttlefish::FileExists(icc_profile_path) ||
387       !cuttlefish::FileHasContent(icc_profile_path.c_str())) {
388     ss.clear();
389     ss.str("");
390 
391     if (sim_type == 2) {  // Special sim card for CtsCarrierApiTestCases
392       ss << "etc/modem_simulator/files/iccprofile_for_sim" << service_id_
393           << "_for_CtsCarrierApiTestCases.xml";
394     } else {
395       ss << "etc/modem_simulator/files/iccprofile_for_sim" << service_id_ << ".xml";
396     }
397 
398     auto etc_file_path =
399         cuttlefish::modem::DeviceConfig::DefaultHostArtifactsPath(ss.str());
400     if (!cuttlefish::FileExists(etc_file_path) || !cuttlefish::FileHasContent(etc_file_path)) {
401       sim_status_ = SIM_STATUS_ABSENT;
402       return;
403     }
404     file = etc_file_path;
405   }
406 
407   sim_file_system_.file_path = icc_profile_path;
408   auto err = sim_file_system_.doc.LoadFile(file.c_str());
409   if (err != tinyxml2::XML_SUCCESS) {
410     LOG(ERROR) << "Unable to load XML file '" << file << " ', error " << err;
411     sim_status_ = SIM_STATUS_ABSENT;
412     return;
413   }
414 
415   XMLElement *root = sim_file_system_.GetRootElement();
416   if (!root) {
417     LOG(ERROR) << "Unable to find root element: IccProfile";
418     sim_status_ = SIM_STATUS_ABSENT;
419     return;
420   }
421 
422   // Default value if iccprofile not configure pin state
423   sim_status_ = SIM_STATUS_READY;
424   pin1_status_.pin_ = kDefaultPinCode;
425   pin1_status_.puk_ = kDefaultPukCode;
426   pin1_status_.pin_remaining_times_ = kSimPinMaxRetryTimes;
427   pin1_status_.puk_remaining_times_ = kSimPukMaxRetryTimes;
428   pin2_status_.pin_ = kDefaultPinCode;
429   pin2_status_.puk_ = kDefaultPukCode;
430   pin2_status_.pin_remaining_times_ = kSimPinMaxRetryTimes;
431   pin2_status_.puk_remaining_times_ = kSimPukMaxRetryTimes;
432 
433   XMLElement *pin_profile = root->FirstChildElement("PinProfile");
434   if (pin_profile) {
435     // Pin1 status
436     auto pin_state = pin_profile->FirstChildElement("PINSTATE");
437     if (pin_state) {
438       std::string state = pin_state->GetText();
439       if (state == "PINSTATE_ENABLED_NOT_VERIFIED") {
440         sim_status_ = SIM_STATUS_PIN;
441       } else if (state == "PINSTATE_ENABLED_BLOCKED") {
442         sim_status_ = SIM_STATUS_PUK;
443       }
444     }
445     auto pin_code = pin_profile->FirstChildElement("PINCODE");
446     if (pin_code) {
447       pin1_status_.pin_ = pin_code->GetText();
448     }
449 
450     auto puk_code = pin_profile->FirstChildElement("PUKCODE");
451     if (puk_code) {
452       pin1_status_.puk_ = puk_code->GetText();
453     }
454 
455     auto pin_remaining_times = pin_profile->FirstChildElement("PINREMAINTIMES");
456     if (pin_remaining_times) {
457       pin1_status_.pin_remaining_times_ = std::stoi(pin_remaining_times->GetText());
458     }
459 
460     auto puk_remaining_times = pin_profile->FirstChildElement("PUKREMAINTIMES");
461     if (puk_remaining_times) {
462       pin1_status_.puk_remaining_times_ = std::stoi(puk_remaining_times->GetText());
463     }
464 
465     // Pin2 status
466     auto pin2_code = pin_profile->FirstChildElement("PIN2CODE");
467     if (pin2_code) {
468       pin2_status_.pin_ = pin2_code->GetText();
469     }
470 
471     auto puk2_code = pin_profile->FirstChildElement("PUK2CODE");
472     if (puk2_code) {
473       pin2_status_.puk_ = puk2_code->GetText();
474     }
475 
476     auto pin2_remaining_times = pin_profile->FirstChildElement("PIN2REMAINTIMES");
477     if (pin2_remaining_times) {
478       pin2_status_.pin_remaining_times_ = std::stoi(pin2_remaining_times->GetText());
479     }
480 
481     auto puk2_remaining_times = pin_profile->FirstChildElement("PUK2REMAINTIMES");
482     if (puk2_remaining_times) {
483       pin2_status_.puk_remaining_times_ = std::stoi(puk2_remaining_times->GetText());
484     }
485   }
486 }
487 
InitializeFacilityLock()488 void SimService::InitializeFacilityLock() {
489   /* Default disable */
490   facility_lock_ = {
491       {"SC", FacilityLock(FacilityLock::LockStatus::DISABLE)},
492       {"FD", FacilityLock(FacilityLock::LockStatus::DISABLE)},
493       {"AO", FacilityLock(FacilityLock::LockStatus::DISABLE)},
494       {"OI", FacilityLock(FacilityLock::LockStatus::DISABLE)},
495       {"OX", FacilityLock(FacilityLock::LockStatus::DISABLE)},
496       {"AI", FacilityLock(FacilityLock::LockStatus::DISABLE)},
497       {"IR", FacilityLock(FacilityLock::LockStatus::DISABLE)},
498       {"AB", FacilityLock(FacilityLock::LockStatus::DISABLE)},
499       {"AG", FacilityLock(FacilityLock::LockStatus::DISABLE)},
500       {"AC", FacilityLock(FacilityLock::LockStatus::DISABLE)},
501   };
502 
503   XMLElement *root = sim_file_system_.GetRootElement();
504   if (!root) {
505     LOG(ERROR) << "Unable to find root element: IccProfile";
506     sim_status_ = SIM_STATUS_ABSENT;
507     return;
508   }
509 
510   XMLElement *facility_lock = root->FirstChildElement("FacilityLock");
511   if (!facility_lock) {
512     LOG(ERROR) << "Unable to find element: FacilityLock";
513     return;
514   }
515 
516   for (auto iter = facility_lock_.begin(); iter != facility_lock_.end(); ++iter) {
517     auto lock_status = facility_lock->FirstChildElement(iter->first.c_str());
518     if (lock_status) {
519       std::string state = lock_status->GetText();
520       if (state == "ENABLE") {
521         iter->second.lock_status = FacilityLock::LockStatus::ENABLE;
522       }
523     }
524   }
525 }
526 
SavePinStateToIccProfile()527 void SimService::SavePinStateToIccProfile() {
528   XMLElement *root = sim_file_system_.GetRootElement();
529   if (!root) {
530     LOG(ERROR) << "Unable to find root element: IccProfile";
531     sim_status_ = SIM_STATUS_ABSENT;
532     return;
533   }
534 
535   XMLElement *pin_profile = root->FirstChildElement("PinProfile");
536   if (!pin_profile) {
537     pin_profile = sim_file_system_.AppendNewElement(root, "PinProfile");
538   }
539 
540   const char* text = "PINSTATE_UNKNOWN";
541 
542   if (sim_status_ == SIM_STATUS_PUK) {
543     text = "PINSTATE_ENABLED_BLOCKED";
544   } else {
545     auto iter = facility_lock_.find("SC");
546     if (iter != facility_lock_.end()) {
547       if (iter->second.lock_status == FacilityLock::ENABLE) {
548         text = "PINSTATE_ENABLED_NOT_VERIFIED";
549       }
550     }
551   }
552 
553   // Pin1 status
554   auto pin_state = pin_profile->FirstChildElement("PINSTATE");
555   if (!pin_state) {
556     pin_state = sim_file_system_.AppendNewElementWithText(pin_profile, "PINSTATE", text);
557   } else {
558     pin_state->SetText(text);
559   }
560 
561   auto pin_code = pin_profile->FirstChildElement("PINCODE");
562   if (!pin_code) {
563     pin_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PINCODE",
564         pin1_status_.pin_.c_str());
565   } else {
566     pin_code->SetText(pin1_status_.pin_.c_str());
567   }
568 
569   auto puk_code = pin_profile->FirstChildElement("PUKCODE");
570   if (!puk_code) {
571     puk_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PUKCODE",
572         pin1_status_.puk_.c_str());
573   } else {
574     puk_code->SetText(pin1_status_.puk_.c_str());
575   }
576 
577   std::stringstream ss;
578   ss << pin1_status_.pin_remaining_times_;
579 
580   auto pin_remaining_times = pin_profile->FirstChildElement("PINREMAINTIMES");
581   if (!pin_remaining_times) {
582     pin_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
583         "PINREMAINTIMES", ss.str().c_str());
584   } else {
585     pin_remaining_times->SetText(ss.str().c_str());
586   }
587   ss.clear();
588   ss.str("");
589   ss << pin1_status_.puk_remaining_times_;
590 
591   auto puk_remaining_times = pin_profile->FirstChildElement("PUKREMAINTIMES");
592   if (!puk_remaining_times) {
593     puk_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
594         "PUKREMAINTIMES", ss.str().c_str());
595   } else {
596     puk_remaining_times->SetText(ss.str().c_str());
597   }
598 
599   // Pin2 status
600   auto pin2_code = pin_profile->FirstChildElement("PIN2CODE");
601   if (!pin2_code) {
602     pin2_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PIN2CODE",
603         pin2_status_.pin_.c_str());
604   } else {
605     pin2_code->SetText(pin2_status_.pin_.c_str());
606   }
607 
608   auto puk2_code = pin_profile->FirstChildElement("PUK2CODE");
609   if (!puk2_code) {
610     puk2_code = sim_file_system_.AppendNewElementWithText(pin_profile, "PUK2CODE",
611         pin2_status_.puk_.c_str());
612   } else {
613     puk2_code->SetText(pin2_status_.puk_.c_str());
614   }
615 
616   ss.clear();
617   ss.str("");
618   ss << pin2_status_.pin_remaining_times_;
619 
620   auto pin2_remaining_times = pin_profile->FirstChildElement("PIN2REMAINTIMES");
621   if (!pin2_remaining_times) {
622     pin2_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
623         "PINREMAINTIMES", ss.str().c_str());
624   } else {
625     pin2_remaining_times->SetText(ss.str().c_str());
626   }
627   ss.clear();
628   ss.str("");
629   ss << pin2_status_.puk_remaining_times_;
630 
631   auto puk2_remaining_times = pin_profile->FirstChildElement("PUK2REMAINTIMES");
632   if (!puk2_remaining_times) {
633     puk2_remaining_times = sim_file_system_.AppendNewElementWithText(pin_profile,
634         "PUK2REMAINTIMES", ss.str().c_str());
635   } else {
636     puk2_remaining_times->SetText(ss.str().c_str());
637   }
638 
639   // Save file
640   sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
641 }
642 
SaveFacilityLockToIccProfile()643 void SimService::SaveFacilityLockToIccProfile() {
644   XMLElement *root = sim_file_system_.GetRootElement();
645   if (!root) {
646     LOG(ERROR) << "Unable to find root element: IccProfile";
647     sim_status_ = SIM_STATUS_ABSENT;
648     return;
649   }
650 
651   XMLElement *facility_lock = root->FirstChildElement("FacilityLock");
652   if (!facility_lock) {
653     facility_lock = sim_file_system_.AppendNewElement(root, "FacilityLock");
654   }
655 
656   const char* text = "DISABLE";
657 
658   for (auto iter = facility_lock_.begin(); iter != facility_lock_.end(); ++iter) {
659     if (iter->second.lock_status == FacilityLock::LockStatus::ENABLE) {
660       text = "ENABLE";
661     } else {
662       text = "DISABLE";
663     }
664     auto element = facility_lock->FirstChildElement(iter->first.c_str());
665     if (!element) {
666       element = sim_file_system_.AppendNewElementWithText(facility_lock,
667           iter->first.c_str(), text);
668     } else {
669       element->SetText(text);
670     }
671   }
672 
673   sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
674 
675   InitializeSimFileSystemAndSimState();
676   InitializeFacilityLock();
677 }
678 
IsFDNEnabled()679 bool SimService::IsFDNEnabled() {
680   auto iter = facility_lock_.find("FD");
681   if (iter != facility_lock_.end() &&
682       iter->second.lock_status == FacilityLock::LockStatus::ENABLE) {
683     return true;
684   }
685   return false;
686 }
687 
IsFixedDialNumber(std::string_view number)688 bool SimService::IsFixedDialNumber(std::string_view number) {
689   XMLElement* root = sim_file_system_.GetRootElement();
690   if (!root) {
691     return false;
692   }
693 
694   auto path = SimFileSystem::GetUsimEFPath(SimFileSystem::EFId::EF_FDN);
695 
696   size_t pos = 0;
697   auto parent = root;
698   while (pos < path.length()) {
699     std::string sub_path(path.substr(pos, 4));
700     auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
701     if (!app) {
702       return false;
703     }
704     pos += 4;
705     parent = app;
706   }
707 
708   XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", "6F3B");
709   if (!ef) {
710     return false;
711   }
712 
713   XMLElement *final = ef->FirstChildElement("SIMIO");
714   while (final) {
715     std::string record = final->GetText();
716     int footerOffset = record.length() - kFooterSizeBytes * 2;
717     int numberLength = (record[footerOffset] - '0') * 16 +
718                         record[footerOffset + 1] - '0';
719     if (numberLength > kMaxNumberSizeBytes) {  // Invalid number length
720       final = final->NextSiblingElement("SIMIO");
721       continue;
722     }
723 
724     std::string bcd_fdn = "";
725     if (numberLength * 2 == 16) {  // Skip Type(91) and Country Code(68)
726       bcd_fdn = record.substr(footerOffset + 6, numberLength * 2 - 4);
727     } else {  // Skip Type(81)
728       bcd_fdn = record.substr(footerOffset + 4, numberLength * 2 - 2);
729     }
730 
731     std::string fdn = PDUParser::BCDToString(bcd_fdn);
732     if (fdn == number) {
733       return true;
734     }
735     final = final->NextSiblingElement("SIMIO");
736   }
737 
738   return false;
739 }
740 
GetIccProfile()741 XMLElement* SimService::GetIccProfile() {
742   return sim_file_system_.GetRootElement();
743 }
744 
GetPhoneNumber()745 std::string SimService::GetPhoneNumber() {
746   XMLElement* final = GetPhoneNumberElement();
747   if (!final) {
748     return "";
749   }
750   std::string record = final->GetText();
751   int footerOffset = record.length() - kFooterSizeBytes * 2;
752   int numberLength = (record[footerOffset] - '0') * 16 +
753                       record[footerOffset + 1] - '0';
754   if (numberLength > kMaxNumberSizeBytes) {  // Invalid number length
755     return "";
756   }
757 
758   std::string bcd_number = "";
759   if (numberLength * 2 == 16) {  // Skip Type(91) and Country Code(68)
760     bcd_number = record.substr(footerOffset + 6, numberLength * 2 - 4);
761   } else {  // Skip Type(81)
762     bcd_number = record.substr(footerOffset + 4, numberLength * 2 - 2);
763   }
764 
765   return PDUParser::BCDToString(bcd_number);
766 }
767 
SetPhoneNumber(std::string_view number)768 bool SimService::SetPhoneNumber(std::string_view number) {
769   if (number.size() > kMaxNumberSizeBytes) {  // Invalid number length
770     return false;
771   }
772   XMLElement* elem = GetPhoneNumberElement();
773   if (!elem) {
774     return false;
775   }
776   std::string record = elem->GetText();
777   int footerOffset = record.length() - kFooterSizeBytes * 2;
778   std::string bcd_number = PDUParser::StringToBCD(number);
779   int newLength = 0;
780   // Skip Type(91) and Country Code(68)
781   if (number.size() == 12 && number.compare("68") == 0) {
782     record.replace(footerOffset + 6, bcd_number.size(), bcd_number);
783     newLength = 8;
784   } else { // Skip Type(81)
785     record.replace(footerOffset + 4, bcd_number.size(), bcd_number);
786     newLength = (bcd_number.size() + 2) / 2;
787   }
788 
789   record[footerOffset] = '0' + newLength / 16;
790   record[footerOffset + 1] = '0' + (newLength % 16);
791 
792   elem->SetText(record.c_str());
793   sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
794   return true;
795 }
796 
GetSimStatus() const797 SimService::SimStatus SimService::GetSimStatus() const {
798   return sim_status_;
799 }
800 
GetSimOperator()801 std::string SimService::GetSimOperator() {
802   XMLElement* root = sim_file_system_.GetRootElement();
803   if (!root) {
804     return "";
805   }
806 
807   XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
808   if (!mf) {
809     return "";
810   }
811 
812   XMLElement* df = SimFileSystem::FindAttribute(mf, "path", DF_ADF);
813   if (!df) {
814     return "";
815   }
816 
817   XMLElement* ef = SimFileSystem::FindAttribute(df, "id", "6F07");
818   if (!ef) {
819     return "";
820   }
821 
822   XMLElement* cimi = ef->FirstChildElement("CIMI");
823   if (!cimi) {
824     return "";
825   }
826 
827   std::string imsi = cimi->GetText();
828 
829   ef = SimFileSystem::FindAttribute(df, "id", "6FAD");
830   if (!ef) {
831     return "";
832   }
833 
834   XMLElement *sim_io = ef->FirstChildElement("SIMIO");
835   while (sim_io) {
836     const XMLAttribute *attr_cmd = sim_io->FindAttribute("cmd");
837     std::string attr_value = attr_cmd ? attr_cmd->Value() : "";
838     if (attr_cmd && attr_value == "B0") {
839       break;
840     }
841 
842     sim_io = sim_io->NextSiblingElement("SIMIO");
843   }
844 
845   if (!sim_io) {
846     return "";
847   }
848 
849   std::string length = sim_io->GetText();
850   int mnc_size = std::stoi(length.substr(length.size() -2));
851 
852   return imsi.substr(0, 3 + mnc_size);
853 }
854 
SetupDependency(NetworkService * net)855 void SimService::SetupDependency(NetworkService* net) {
856   network_service_ = net;
857 }
858 
859 /**
860  * AT+CPIN
861  *   Set command sends to the MT a password which is necessary before it can be
862  * operated.
863  *   Read command returns an alphanumeric string indicating whether some
864  * password is required or not.
865  *
866  * Command                            Possible response(s)
867  * +CPIN=<pin>[,<newpin>]              +CME ERROR: <err>
868  * +CPIN?                              +CPIN: <code>
869  *                                     +CME ERROR: <err>
870  * <pin>, <newpin>: string type values.
871  * <code> values reserved by the present document:
872  *    READY   MT is not pending for any password
873  *   SIM PIN  MT is waiting SIM PIN to be given
874  *   SIM PUK  MT is waiting SIM PUK to be given
875  *
876  * see RIL_REQUEST_GET_SIM_STATUS in RIL
877  */
HandleSIMStatusReq(const Client & client)878 void SimService::HandleSIMStatusReq(const Client& client) {
879   std::vector<std::string> responses;
880   auto iter = gSimStatusResponse.find(sim_status_);
881   if (iter != gSimStatusResponse.end()) {
882     responses.push_back(iter->second);
883     responses.push_back("OK");
884   } else {
885     sim_status_ = SIM_STATUS_ABSENT;
886     responses.push_back(kCmeErrorSimNotInserted);
887   }
888   client.SendCommandResponse(responses);
889 }
890 
891 /**
892  * AT+CRSM
893  *   By using this command instead of Generic SIM Access +CSIM TE application
894  *   has easier but more limited access to the SIM database.
895  *
896  *   Command                                Possible response(s)
897  * +CRSM=<command>[,<fileid>                +CRSM: <sw1>,<sw2>[,<response>]
898  * [,<P1>,<P2>,<P3>[,<data>[,<pathid>]]]]   +CME ERROR: <err>
899  *
900  * <command>: (command passed on by the MT to the SIM; refer 3GPP TS 51.011 [28]):
901  *   176 READ BINARY
902  *   178 READ RECORD
903  *   192 GET RESPONSE
904  *   214 UPDATE BINARY
905  *   220 UPDATE RECORD
906  *   242 STATUS
907  *   203 RETRIEVE DATA
908  *   219 SET DATA
909  *
910  * <fileid>: integer type; this is the identifier of a elementary datafile on SIM.
911  *           Mandatory for every command except STATUS.
912  *
913  * <P1>, <P2>, <P3>: integer type; parameters passed on by the MT to the SIM.
914  *                   These parameters are mandatory for every command,
915  *                   except GET RESPONSE and STATUS.
916  *
917  * <data>: information which shall be written to the SIM (hexadecimal character format).
918  *
919  * <pathid>: string type; contains the path of an elementary file on the SIM/UICC
920  *           in hexadecimal format.
921  *
922  * <sw1>, <sw2>: integer type; information from the SIM about the execution of
923  *               the actual command.
924  *
925  * <response>: response of a successful completion of the command previously issued
926  *             (hexadecimal character format; refer +CSCS).
927  */
HandleSIM_IO(const Client & client,const std::string & command)928 void SimService::HandleSIM_IO(const Client& client,
929                               const std::string& command) {
930   std::vector<std::string> kFileNotFoud = {"+CRSM: 106,130", "OK"};
931   std::vector<std::string> responses;
932 
933   CommandParser cmd(command);
934   cmd.SkipPrefix();  // skip "AT+CRSM="
935 
936   if (*cmd == "242,0,0,0,0") { //  for cts teset
937     responses.push_back("+CRSM: 144,0,62338202782183023F00A50C80016187010183040007DBF08A01058B062F0601020002C60C90016083010183010A83010D8102FFFF");
938     responses.push_back("OK");
939     client.SendCommandResponse(responses);
940     return;
941   }
942 
943   auto c = cmd.GetNextStrDeciToHex();
944   auto id = cmd.GetNextStrDeciToHex();
945   auto p1 = cmd.GetNextStrDeciToHex();
946   auto p2 = cmd.GetNextStrDeciToHex();
947   auto p3 = cmd.GetNextStrDeciToHex();
948 
949   auto data = cmd.GetNextStr(',');
950   std::string path(cmd.GetNextStr());
951 
952   XMLElement *root = sim_file_system_.GetRootElement();
953   if (!root) {
954     LOG(ERROR) << "Unable to find root element: IccProfile";
955     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
956     return;
957   }
958 
959   SimFileSystem::EFId fileid = (SimFileSystem::EFId)std::stoi(id, nullptr, 16);
960   if (path == "") {
961     path = SimFileSystem::GetUsimEFPath(fileid);
962   }
963   // EF_ADN under DF_PHONEBOOK is mapped to EF_ADN under DF_TELECOM per
964   // 3GPP TS 31.102 4.4.2
965   if (fileid == SimFileSystem::EF_ADN &&
966       path == SimFileSystem::GetUsimEFPath(fileid)) {
967     id = "4F3A";
968     path = MF_SIM + DF_TELECOM + DF_PHONEBOOK;
969   }
970 
971   size_t pos = 0;
972   auto parent = root;
973   while (pos < path.length()) {
974     std::string sub_path(path.substr(pos, 4));
975     auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
976     if (!app) {
977       client.SendCommandResponse(kFileNotFoud);
978       return;
979     }
980     pos += 4;
981     parent = app;
982   }
983 
984   XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", id);
985   if (!ef) {
986     client.SendCommandResponse(kFileNotFoud);
987     return;
988   }
989 
990   XMLElement *final = ef->FirstChildElement("SIMIO");
991   while (final) {
992     const XMLAttribute *attr_cmd = final->FindAttribute("cmd");
993     const XMLAttribute *attr_p1 = final->FindAttribute("p1");
994     const XMLAttribute *attr_p2 = final->FindAttribute("p2");
995     const XMLAttribute *attr_p3 = final->FindAttribute("p3");
996     const XMLAttribute *attr_data = final->FindAttribute("data");
997 
998     if (c != "DC" && c != "D6") {  // Except UPDATE RECORD or UPDATE BINARY
999       if ((attr_cmd && attr_cmd->Value() != c) ||
1000           (attr_data && attr_data->Value() != data)) {
1001         final = final->NextSiblingElement("SIMIO");
1002         continue;
1003       }
1004     }
1005     if (attr_p1 && attr_p1->Value() == p1 &&
1006         attr_p2 && attr_p2->Value() == p2 &&
1007         attr_p3 && attr_p3->Value() == p3) {
1008       break;
1009     }
1010     final = final->NextSiblingElement("SIMIO");
1011   }
1012 
1013   if (!final) {
1014     client.SendCommandResponse(kFileNotFoud);
1015     return;
1016   }
1017 
1018   std::string response = "+CRSM: ";
1019   if (c == "DC" || c == "D6") {
1020     std::string temp = "144,0,";
1021     temp += data;
1022     final->SetText(temp.c_str());
1023     sim_file_system_.doc.SaveFile(sim_file_system_.file_path.c_str());
1024     response.append("144,0");
1025   } else {
1026     response.append(final->GetText());
1027   }
1028 
1029   responses.push_back(response);
1030   responses.push_back("OK");
1031   client.SendCommandResponse(responses);
1032 }
1033 
OnSimStatusChanged()1034 void SimService::OnSimStatusChanged() {
1035   auto ptr = network_service_;
1036   if (ptr) {
1037     ptr->OnSimStatusChanged(sim_status_);
1038   }
1039 }
1040 
GetPhoneNumberElement()1041 XMLElement* SimService::GetPhoneNumberElement() {
1042   XMLElement* root = sim_file_system_.GetRootElement();
1043   if (!root) {
1044     return nullptr;
1045   }
1046 
1047   auto path = SimFileSystem::GetUsimEFPath(SimFileSystem::EFId::EF_MSISDN);
1048 
1049   size_t pos = 0;
1050   auto parent = root;
1051   while (pos < path.length()) {
1052     std::string sub_path(path.substr(pos, 4));
1053     auto app = SimFileSystem::FindAttribute(parent, "path", sub_path);
1054     if (!app) {
1055       return nullptr;
1056     }
1057     pos += 4;
1058     parent = app;
1059   }
1060 
1061   XMLElement* ef = SimFileSystem::FindAttribute(parent, "id", "6F40");
1062   if (!ef) {
1063     return nullptr;
1064   }
1065 
1066   return SimFileSystem::FindAttribute(ef, "cmd", "B2");
1067 }
1068 
checkPin1AndAdjustSimStatus(std::string_view pin)1069 bool SimService::checkPin1AndAdjustSimStatus(std::string_view pin) {
1070   if (pin1_status_.VerifyPIN(pin) == true) {
1071     sim_status_ = SIM_STATUS_READY;
1072     OnSimStatusChanged();
1073     return true;
1074   }
1075 
1076   if (pin1_status_.pin_remaining_times_ <= 0) {
1077     sim_status_ = SIM_STATUS_PUK;
1078     OnSimStatusChanged();
1079   }
1080 
1081   return false;
1082 }
1083 
1084 /* AT+CSIM */
HandleCSIM_IO(const Client & client,const std::string & command)1085 void SimService::HandleCSIM_IO(const Client& client,
1086                               const std::string& command) {
1087   std::vector<std::string> responses;
1088 
1089   CommandParser cmd(command);
1090   cmd.SkipPrefix();  // skip "AT+CSIM="
1091 
1092   cmd.SkipComma();
1093   auto data = cmd.GetNextStr();
1094 
1095   XMLElement *root = sim_file_system_.GetRootElement();
1096   if (!root) {
1097     LOG(ERROR) << "Unable to find root element: IccProfile";
1098     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1099     return;
1100   }
1101   // Get aid
1102   XMLElement* df = SimFileSystem::FindAttribute(root, "aid", "CSIM");
1103   if (!df) {
1104     client.SendCommandResponse(kCmeErrorNotFound);
1105     return;
1106   }
1107 
1108   std::string data_value(data);
1109   if (data_value.length() > 10) {  // for open channel with csim
1110       responses.push_back("+CSIM: 4,9000");
1111       responses.push_back("OK");
1112       client.SendCommandResponse(responses);
1113       return;
1114   }
1115   XMLElement* final = SimFileSystem::FindAttribute(df, "cmd", data_value);
1116   if (!final) {
1117     client.SendCommandResponse(kCmeErrorNotFound);
1118     return;
1119   }
1120 
1121   auto id = data_value.substr(data_value.length() - 2, 2);
1122 
1123   std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
1124   for (; iter != logical_channels_.end(); ++iter) {
1125     if (!iter->is_open) {
1126       break;
1127     }
1128   }
1129 
1130   if (iter != logical_channels_.end() && iter->session_id ==stoi(id)) {
1131     iter->is_open = true;
1132     iter->df_name = "CSIM";
1133   }
1134 
1135   std::stringstream ss;
1136   ss << "+CSIM: " << final->GetText();
1137 
1138   responses.push_back(ss.str());
1139   responses.push_back("OK");
1140   client.SendCommandResponse(responses);
1141 }
1142 
ChangePin1AndAdjustSimStatus(PinStatus::ChangeMode mode,std::string_view pin,std::string_view new_pin)1143 bool SimService::ChangePin1AndAdjustSimStatus(PinStatus::ChangeMode mode,
1144                                               std::string_view pin,
1145                                               std::string_view new_pin) {
1146   if (pin1_status_.ChangePIN(mode, pin, new_pin) == true) {
1147     sim_status_ = SIM_STATUS_READY;
1148     OnSimStatusChanged();
1149     return true;
1150   }
1151   if (sim_status_ == SIM_STATUS_READY && pin1_status_.pin_remaining_times_ <= 0) {
1152     sim_status_ = SIM_STATUS_PIN;
1153     OnSimStatusChanged();
1154   } else if (sim_status_ == SIM_STATUS_PIN && pin1_status_.puk_remaining_times_ <= 0) {
1155     sim_status_ = SIM_STATUS_ABSENT;
1156     OnSimStatusChanged();
1157   }
1158   return false;
1159 }
1160 
HandleChangeOrEnterPIN(const Client & client,const std::string & command)1161 void SimService::HandleChangeOrEnterPIN(const Client& client,
1162                                         const std::string& command) {
1163   std::vector<std::string> responses;
1164 
1165   CommandParser cmd(command);
1166   cmd.SkipPrefix();  // skip "AT+CPIN="
1167   switch (sim_status_) {
1168     case SIM_STATUS_ABSENT:
1169       responses.push_back(kCmeErrorSimNotInserted);
1170       break;
1171     case SIM_STATUS_NOT_READY:
1172       responses.push_back(kCmeErrorSimBusy);
1173       break;
1174     case SIM_STATUS_READY: {
1175       /*
1176        * this may be a request to change the PIN with pin and new pin:
1177        *    AT+CPIN=pin,newpin
1178        * or a request to enter the PIN2
1179        *    AT+CPIN=pin2
1180        */
1181       auto pos = cmd->find(',');
1182       if (pos != std::string_view::npos) {  // change pin with new pin
1183         auto pin = cmd.GetNextStr(',');
1184         auto new_pin = *cmd;
1185 
1186         if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PIN, pin, new_pin)) {
1187           responses.push_back("OK");
1188         } else {
1189           responses.push_back(kCmeErrorIncorrectPassword);  /* incorrect PIN */
1190         }
1191       } else {  // verify pin2
1192         if (pin2_status_.VerifyPIN(*cmd) == true) {
1193           responses.push_back("OK");
1194         } else {
1195           responses.push_back(kCmeErrorIncorrectPassword);  /* incorrect PIN2 */
1196         }
1197       }
1198       break;
1199     }
1200     case SIM_STATUS_PIN: {  /* waiting for PIN */
1201       if (checkPin1AndAdjustSimStatus(*cmd) == true) {
1202         responses.push_back("OK");
1203       } else {
1204         responses.push_back(kCmeErrorIncorrectPassword);
1205       }
1206       break;
1207     }
1208     case SIM_STATUS_PUK: {
1209       /*
1210        * this may be a request to unlock the puk with new pin:
1211        *    AT+CPIN=puk,newpin
1212        */
1213       auto pos = cmd->find(',');
1214       if (pos != std::string_view::npos) {
1215         auto puk = cmd.GetNextStr(',');
1216         auto new_pin = *cmd;
1217         if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PUK, puk, new_pin)) {
1218           responses.push_back("OK");
1219         } else {
1220           responses.push_back(kCmeErrorIncorrectPassword);
1221         }
1222       } else {
1223         responses.push_back(kCmeErrorOperationNotAllowed);
1224       }
1225       break;
1226     }
1227     default:
1228       responses.push_back(kCmeErrorOperationNotAllowed);
1229       break;
1230   }
1231 
1232   client.SendCommandResponse(responses);
1233 }
1234 
1235 /**
1236  * AT+CIMI
1237  *   Execution command causes the TA to return <IMSI>, which is intended to
1238  * permit the TE to identify the individual SIM card or active application in
1239  * the UICC (GSM or USIM) which is attached to MT.
1240  *
1241  * Command                            Possible response(s)
1242  * +CIMI                               <IMSI>
1243  *                                     +CME ERROR: <err>
1244  *
1245  * <IMSI>: International Mobile Subscriber Identity (string without double quotes)
1246  *
1247  * see RIL_REQUEST_GET_IMSI in RIL
1248  */
HandleGetIMSI(const Client & client)1249 void SimService::HandleGetIMSI(const Client& client) {
1250   std::vector<std::string> responses;
1251 
1252   XMLElement *root = sim_file_system_.GetRootElement();
1253   if (!root) {
1254     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1255     return;
1256   }
1257 
1258   XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
1259   if (!mf) {
1260     client.SendCommandResponse(kCmeErrorNotFound);
1261     return;
1262   }
1263 
1264   XMLElement* df = SimFileSystem::FindAttribute(mf, "path", DF_ADF);
1265   if (!df) {
1266     client.SendCommandResponse(kCmeErrorNotFound);
1267     return;
1268   }
1269 
1270   XMLElement* ef = SimFileSystem::FindAttribute(df, "id", "6F07");
1271   if (!ef) {
1272     client.SendCommandResponse(kCmeErrorNotFound);
1273     return;
1274   }
1275 
1276   XMLElement *final = ef->FirstChildElement("CIMI");
1277   if (!final) {
1278     client.SendCommandResponse(kCmeErrorNotFound);
1279     return;
1280   }
1281 
1282   responses.push_back(final->GetText());
1283   responses.push_back("OK");
1284   client.SendCommandResponse(responses);
1285 }
1286 
1287 /**
1288  * AT+CICCID
1289  *   Integrated Circuit Card IDentifier (ICCID) is Unique Identifier of the SIM CARD.
1290  *  File is located in the SIM card at EFiccid (0x2FE2).
1291  *
1292  * see RIL_REQUEST_GET_SIM_STATUS in RIL
1293  */
HandleGetIccId(const Client & client)1294 void SimService::HandleGetIccId(const Client& client) {
1295   std::vector<std::string> responses;
1296 
1297   XMLElement *root = sim_file_system_.GetRootElement();
1298   if (!root) {
1299     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1300     return;
1301   }
1302 
1303   XMLElement* mf = SimFileSystem::FindAttribute(root, "path", MF_SIM);
1304   if (!mf) {
1305     client.SendCommandResponse(kCmeErrorNotFound);
1306     return;
1307   }
1308 
1309   XMLElement* ef = SimFileSystem::FindAttribute(mf, "id", "2FE2");
1310   if (!ef) {
1311     client.SendCommandResponse(kCmeErrorNotFound);
1312     return;
1313   }
1314 
1315   XMLElement *final = ef->FirstChildElement("CCID");
1316   if (!final) {
1317     client.SendCommandResponse(kCmeErrorNotFound);
1318     return;
1319   }
1320 
1321   responses.push_back(final->GetText());
1322   responses.push_back("OK");
1323   client.SendCommandResponse(responses);
1324 }
1325 
1326 /*
1327  * AT+CLCK
1328  *   Execute command is used to lock, unlock or interrogate a MT or a network
1329  * facility <fac>.
1330  *
1331  * Command                            Possible response(s)
1332  * +CLCK=<fac>, <mode> [, <password>   OK or +CME ERROR: <err>
1333  *       [, <class>]]                  +CLCK: <status>[,<class1>[<CR><LF>+CLCK:
1334  *                                     <status>,<class2>[...]](when mode=2,it’s
1335  *                                     in inquiry status.)
1336  * <fac> values reserved by the present document:
1337  *    "SC": SIM (lock SIM/UICC card installed in the currently selected card
1338  *          slot) (SIM/UICC asks password in MT power‑up and when this lock
1339  *          command issued).
1340  *    "FD": SIM card or active application in the UICC (GSM or USIM) fixed
1341  *          dialling memory feature (if PIN2 authentication has not been done
1342  *          during the current session, PIN2 is required as <passwd>).
1343  * <mode>: integer type
1344  *      0: unlock
1345  *      1: lock
1346  *      2: query status
1347  * <status>: integer type
1348  *        0: not active
1349  *        1: active
1350  * <passwd>: string type; shall be the same as password specified for the
1351  *           facility from the MT user interface or with command
1352  *           Change Password +CPWD.
1353  * <classx> is a sum of integers each representing a class of information
1354  *          (default 7 - voice, data and fax):
1355  *        1 voice (telephony)
1356  *        2 data
1357  *        4 fax (facsimile services)
1358  *        8 short message service
1359  *       16 data circuit sync
1360  *       32 data circuit async
1361  *       64 dedicated packet access
1362  *      128 dedicated PAD access
1363  *
1364  * see RIL_REQUEST_SET_FACILITY_LOCK in RIL
1365  */
HandleFacilityLock(const Client & client,const std::string & command)1366 void SimService::HandleFacilityLock(const Client& client,
1367                                     const std::string& command) {
1368   CommandParser cmd(command);
1369   std::string lock(cmd.GetNextStr());
1370   int mode = cmd.GetNextInt();
1371   auto password = cmd.GetNextStr();
1372   // Ignore class from RIL
1373 
1374   auto iter = facility_lock_.find(lock);
1375   if (iter == facility_lock_.end()) {
1376     client.SendCommandResponse(kCmeErrorOperationNotSupported);
1377     return;
1378   }
1379 
1380   std::stringstream ss;
1381   std::vector<std::string> responses;
1382   switch (mode) {
1383     case FacilityLock::Mode::QUERY: {
1384       ss << "+CLCK: " << iter->second.lock_status;
1385       responses.push_back(ss.str());
1386       responses.push_back("OK");
1387       break;
1388     }
1389     case FacilityLock::Mode::LOCK:
1390     case FacilityLock::Mode::UNLOCK: {
1391       if (lock == "SC") {
1392         if (checkPin1AndAdjustSimStatus(password) == true) {
1393           iter->second.lock_status = (FacilityLock::LockStatus)mode;
1394           responses.push_back("OK");
1395         } else {
1396           responses.push_back(kCmeErrorIncorrectPassword);
1397         }
1398       } else if (lock == "FD") {
1399         if (pin2_status_.VerifyPIN(password) == true) {
1400           iter->second.lock_status = (FacilityLock::LockStatus)mode;
1401           responses.push_back("OK");
1402         } else {
1403           responses.push_back(kCmeErrorIncorrectPassword);
1404         }
1405       } else {  // Don't need password except 'SC' and 'FD'
1406         iter->second.lock_status = (FacilityLock::LockStatus)mode;
1407         responses.push_back("OK");
1408       }
1409       break;
1410     }
1411     default:
1412       responses.push_back(kCmeErrorInCorrectParameters);
1413       break;
1414   }
1415 
1416   client.SendCommandResponse(responses);
1417 }
1418 
1419 /**
1420  * AT+CCHO
1421  *   The currently selected UICC will open a new logical channel; select the
1422  * application identified by the <dfname> received with this command and return
1423  * a session Id as the response.
1424  *
1425  * Command                            Possible response(s)
1426  * +CCHO=<dfname>                      <sessionid>
1427  *                                     +CME ERROR: <err>
1428  *
1429  * <dfname>: all selectable applications in the UICC are referenced by a DF
1430  *           name coded on 1 to 16 bytes.
1431  * <sessionid>: integer type; a session Id to be used in order to target a
1432  *            specific application on the smart card (e.g. (U)SIM, WIM, ISIM)
1433  *            using logical channels mechanism.
1434  *
1435  * see RIL_REQUEST_SIM_OPEN_CHANNEL in RIL
1436  */
HandleOpenLogicalChannel(const Client & client,const std::string & command)1437 void SimService::HandleOpenLogicalChannel(const Client& client,
1438                                           const std::string& command) {
1439   std::vector<std::string> responses;
1440 
1441   CommandParser cmd(command);
1442   cmd.SkipPrefix();  // skip AT+CCHO=
1443   if (cmd->empty()) {
1444     client.SendCommandResponse(kCmeErrorInCorrectParameters);
1445     return;
1446   }
1447 
1448   XMLElement *root = sim_file_system_.GetRootElement();
1449   if (!root) {
1450     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1451     return;
1452   }
1453 
1454   std::string aid_value(*cmd);
1455   XMLElement* df = SimFileSystem::FindAttribute(root, "aid", aid_value);
1456   if (!df) {
1457     client.SendCommandResponse(kCmeErrorNotFound);
1458     return;
1459   }
1460 
1461   std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
1462   for (; iter != logical_channels_.end(); ++iter) {
1463     if (!iter->is_open) {
1464       break;
1465     }
1466   }
1467 
1468   if (iter != logical_channels_.end()) {
1469     iter->is_open = true;
1470     iter->df_name = *cmd;
1471 
1472     std::stringstream ss;
1473     ss << iter->session_id;
1474     responses.push_back(ss.str());
1475     responses.push_back("OK");
1476   } else {
1477     responses.push_back(kCmeErrorMemoryFull);
1478   }
1479 
1480   client.SendCommandResponse(responses);
1481 }
1482 
1483 /**
1484  * AT+CCHC
1485  *   This command asks the ME to close a communication session with the active
1486  * UICC.
1487  *
1488  * Command                            Possible response(s)
1489  * +CCHC=<sessionid>                   +CCHC
1490  *                                     +CME ERROR: <err>
1491  * <sessionid>: see AT+CCHO
1492  *
1493  * see RIL_REQUEST_SIM_CLOSE_CHANNEL in RIL
1494  */
HandleCloseLogicalChannel(const Client & client,const std::string & command)1495 void SimService::HandleCloseLogicalChannel(const Client& client,
1496                                            const std::string& command) {
1497   std::vector<std::string> responses;
1498 
1499   CommandParser cmd(command);
1500   cmd.SkipPrefix();  // skip AT+CCHC=
1501 
1502   int session_id = cmd.GetNextInt();
1503   std::vector<LogicalChannel>::iterator iter = logical_channels_.begin();
1504   for (; iter != logical_channels_.end(); ++iter) {
1505     if (iter->session_id == session_id) {
1506       break;
1507     }
1508   }
1509 
1510   if (iter != logical_channels_.end() && iter->is_open) {
1511     iter->is_open = false;
1512     iter->df_name.clear();
1513     responses.push_back("+CCHC");
1514     responses.push_back("OK");
1515   } else {
1516     responses.push_back(kCmeErrorNotFound);
1517   }
1518   client.SendCommandResponse(responses);
1519 }
1520 
1521 /**
1522  * AT+CGLA
1523  *   Set command transmits to the MT the <command> it then shall send as it is
1524  * to the selected UICC. In the same manner the UICC <response> shall be sent
1525  * back by the MT to the TA as it is.
1526  *
1527  * Command                            Possible response(s)
1528  * +CGLA=<sessionid>,<length>,         +CGLA: <length>,<response>
1529  *                                     +CME ERROR: <err>
1530  * <sessionid>: AT+CCHO
1531  * <length>: integer type; length of the characters that are sent to TE in
1532  *         <command> or <response> .
1533  * <command>: command passed on by the MT to the UICC in the format as described
1534  *          in 3GPP TS 31.101 [65] (hexadecimal character format; refer +CSCS).
1535  * <response>: response to the command passed on by the UICC to the MT in the
1536  *           format as described in 3GPP TS 31.101 [65] (hexadecimal character
1537  *           format; refer +CSCS).
1538  *
1539  * see RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL in RIL
1540  */
HandleTransmitLogicalChannel(const Client & client,const std::string & command)1541 void SimService::HandleTransmitLogicalChannel(const Client& client,
1542                                               const std::string& command) {
1543   std::vector<std::string> responses;
1544 
1545   CommandParser cmd(command);
1546   cmd.SkipPrefix();  // skip AT+CGLA=
1547 
1548   int session_id = cmd.GetNextInt();
1549   int length = cmd.GetNextInt();
1550   if (cmd->length() != length) {
1551     client.SendCommandResponse(kCmeErrorInCorrectParameters);
1552     return;
1553   }
1554 
1555   // Check if session id is opened
1556   auto iter = logical_channels_.begin();
1557   for (; iter != logical_channels_.end(); ++iter) {
1558     if (iter->session_id == session_id && iter->is_open) {
1559       break;
1560     }
1561   }
1562 
1563   if (iter == logical_channels_.end()) {
1564     client.SendCommandResponse(kCmeErrorInvalidIndex);
1565     return;
1566   }
1567 
1568   XMLElement *root = sim_file_system_.GetRootElement();
1569   if (!root) {
1570     client.SendCommandResponse(kCmeErrorOperationNotAllowed);
1571     return;
1572   }
1573 
1574   // Get aid
1575   XMLElement* df = SimFileSystem::FindAttribute(root, "aid", iter->df_name);
1576   if (!df) {
1577     client.SendCommandResponse(kCmeErrorNotFound);
1578     return;
1579   }
1580 
1581   if (iter->df_name != "CSIM") {
1582     std::string command_vaule(*cmd);
1583     if (command_vaule.substr(2, 2) == "a4") {
1584       last_file_id_ = command_vaule.substr(command_vaule.length() - 4, 4);
1585     }
1586       df = SimFileSystem::FindAttribute(df, "id", last_file_id_);
1587       if (!df) {
1588       client.SendCommandResponse(kCmeErrorNotFound);
1589       return;
1590     }
1591   }
1592 
1593   std::string attr_value(*cmd);
1594   XMLElement* final = SimFileSystem::FindAttribute(df, "cmd", attr_value);
1595   if (!final) {
1596     client.SendCommandResponse(kCmeErrorNotFound);
1597     return;
1598   }
1599 
1600   std::stringstream ss;
1601   ss << "+CGLA: " << final->GetText();
1602   responses.push_back(ss.str());
1603   responses.push_back("OK");
1604   client.SendCommandResponse(responses);
1605 }
1606 
1607 /**
1608  * AT+CPWD
1609  *   Action command sets a new password for the facility lock function defined
1610  * by command Facility Lock +CLCK
1611  *
1612  * Command                              Possible response(s)
1613  * +CPWD=<fac>,<oldpwd>,<newpwd>          +CME ERROR: <err>
1614  *
1615  * <fac>:
1616  *   "P2"  SIM PIN2
1617  *   refer Facility Lock +CLCK for other values
1618  * <oldpwd>, <newpwd>:
1619  *   string type; <oldpwd> shall be the same as password specified for the
1620  *   facility from the MT user interface or with command Change Password +CPWD
1621  *   and <newpwd> is the new password; maximum length of password can be determined
1622  *   with <pwdlength>
1623  * <pwdlength>: integer type maximum length of the password for the facility
1624  */
HandleChangePassword(const Client & client,const std::string & command)1625 void SimService::HandleChangePassword(const Client& client,
1626                                       const std::string& command) {
1627   std::string response = kCmeErrorIncorrectPassword;
1628 
1629   CommandParser cmd(command);
1630   cmd.SkipPrefix();
1631   auto lock = cmd.GetNextStr();
1632   auto old_password = cmd.GetNextStr();
1633   auto new_password = cmd.GetNextStr();
1634 
1635   if (lock == "SC") {
1636     if (ChangePin1AndAdjustSimStatus(PinStatus::WITH_PIN, old_password, new_password)) {
1637       response = "OK";
1638     }
1639   } else if (lock == "P2" || lock == "FD") {
1640     if (pin2_status_.ChangePIN(PinStatus::WITH_PIN, old_password, new_password)) {
1641       response = "OK";
1642     }
1643   } else {
1644     response = kCmeErrorOperationNotSupported;;
1645   }
1646 
1647   client.SendCommandResponse(response);
1648 }
1649 
1650 /**
1651  * AT+CPINR
1652  *   Execution command cause the MT to return the number of remaining PIN retries
1653  * for the MT passwords with intermediate result code
1654  *
1655  * Command                        Possible response(s)
1656  * +CPINR[=<sel_code>]            +CPINR: <code>,<retries>[,<default_retries>]
1657  *
1658  * <retries>:
1659  *   integer type. Number of remaining retries per PIN.
1660  * <default_retries>:
1661  *   integer type. Number of default/initial retries per PIN.
1662  * <code>:
1663  *   Type of PIN. All values listed under the description of the AT+CPIN command
1664  * <sel_code>: String type. Same values as for the <code> and <ext_code> parameters.
1665  *   these values are strings and shall be indicated within double quotes.
1666  */
HandleQueryRemainTimes(const Client & client,const std::string & command)1667 void SimService::HandleQueryRemainTimes(const Client& client,
1668                                         const std::string& command) {
1669   std::vector<std::string> responses;
1670   std::stringstream ss;
1671 
1672   CommandParser cmd(command);
1673   cmd.SkipPrefix();
1674   auto lock_type = cmd.GetNextStr();
1675 
1676   if (lock_type == "SIM PIN") {
1677     ss << "+CPINR: SIM PIN," << pin1_status_.pin_remaining_times_ << ","
1678                              << kSimPinMaxRetryTimes;
1679   } else if (lock_type == "SIM PUK") {
1680     ss << "+CPINR: SIM PUK," << pin1_status_.puk_remaining_times_ << ","
1681                              << kSimPukMaxRetryTimes;
1682   } else if (lock_type == "SIM PIN2") {
1683     ss << "+CPINR: SIM PIN2," << pin2_status_.pin_remaining_times_ << ","
1684                               << kSimPinMaxRetryTimes;
1685   } else if (lock_type == "SIM PUK2") {
1686     ss << "+CPINR: SIM PUK2," << pin2_status_.puk_remaining_times_ << ","
1687             << kSimPukMaxRetryTimes;
1688   } else {
1689     responses.push_back(kCmeErrorInCorrectParameters);
1690     client.SendCommandResponse(responses);
1691     return;
1692   }
1693 
1694   responses.push_back(ss.str());
1695   responses.push_back("OK");
1696   client.SendCommandResponse(responses);
1697 }
1698 
1699 /**
1700  * see
1701  *   RIL_REQUEST_CDMA_SET_SUBSCRIPTION or
1702  *   RIL_REQUEST_CDMA_GET_SUBSCRIPTION in RIL
1703  */
HandleCdmaSubscriptionSource(const Client & client,const std::string & command)1704 void SimService::HandleCdmaSubscriptionSource(const Client& client,
1705                                               const std::string& command) {
1706   std::vector<std::string> responses;
1707 
1708   CommandParser cmd(command);
1709   if (*cmd == "AT+CCSS?") {  // Query
1710     std::stringstream ss;
1711     ss << "+CCSS: " << cdma_subscription_source_;
1712     responses.push_back(ss.str());
1713   } else { // Set
1714     cdma_subscription_source_ = cmd.GetNextInt();
1715   }
1716   responses.push_back("OK");
1717   client.SendCommandResponse(responses);
1718 }
1719 
1720 /**
1721  * see
1722  *   RIL_REQUEST_CDMA_SET_ROAMNING_PREFERENCE or
1723  *   RIL_REQUEST_CDMA_GET_ROAMNING_PREFERENCE in RIL
1724  */
HandleCdmaRoamingPreference(const Client & client,const std::string & command)1725 void SimService::HandleCdmaRoamingPreference(const Client& client,
1726                                              const std::string& command) {
1727   std::vector<std::string> responses;
1728 
1729   CommandParser cmd(command);
1730   if (*cmd == "AT+WRMP?") {  // Query
1731     std::stringstream ss;
1732     ss << "+WRMP: " << cdma_roaming_preference_;
1733     responses.push_back(ss.str());
1734   } else { // Set
1735     cdma_roaming_preference_ = cmd.GetNextInt();
1736   }
1737   responses.push_back("OK");
1738   client.SendCommandResponse(responses);
1739 }
1740 
HandleSimAuthentication(const Client & client,const std::string & command)1741 void SimService::HandleSimAuthentication(const Client& client,
1742                                              const std::string& command) {
1743   std::vector<std::string> responses;
1744 
1745   CommandParser cmd(command);
1746   cmd.SkipPrefix();
1747 
1748   // Input format: ^MBAU=<RAND>[,<AUTN>]
1749   auto cmds = cmd.GetNextStr();
1750   // Output format: ^MBAU: <STATUS>[,<KC>,<SRES>][,<CK>,<IK>,<RES/AUTS>]
1751   std::stringstream ss;
1752 
1753   // Authentication challenges done in CTS.
1754   if (cmds == "2713AB0BA8E8E7D8F1D74545BA03F563") {
1755     // CarrierApiTest#testGetIccAuthentication (base64Challenge)
1756     ss << "^MBAU: 0,8F2980FC3872FF89,E9620240";
1757   } else if (cmds == "C3718EC16B3C2A66F8A7200A64069F04") {
1758     // CarrierApiTest#testGetIccAuthentication (base64Challenge2)
1759     ss << "^MBAU: 0,CFDA6C980502DA48,F7E53577";
1760   } else if (cmds == "11111111111111111111111111111111") {
1761     // CarrierApiTest#testEapSimAuthentication
1762     ss << "^MBAU: 0,0000000000000000,00000000";
1763   } else if (cmds == "11111111111111111111111111111111,12351417161900001130131215141716") {
1764     // CarrierApiTest#testEapAkaAuthentication
1765     // Note: the "DB" prefix gets appended where the RIL parses this response.
1766     ss << "^MBAU: 0,111013121514171619181B1A1D1C1F1E,1013121514171619181B1A1D1C1F1E11,"
1767           "13121514171619181B1A1D1C1F1E1110";
1768   }
1769 
1770   responses.push_back(ss.str());
1771   responses.push_back("OK");
1772   client.SendCommandResponse(responses);
1773 }
1774 
HandlePhoneNumberUpdate(const Client & client,const std::string & command)1775 void SimService::HandlePhoneNumberUpdate(const Client& client,
1776                                          const std::string& command) {
1777   (void)client;
1778   CommandParser cmd(command);
1779   cmd.SkipWhiteSpace();
1780   SetPhoneNumber(cmd.GetNextStr(' '));
1781 }
1782 
1783 }  // namespace cuttlefish
1784