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