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