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