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