SIMRecords.java revision d26924d8d7c29cbcbfaf43c408d3b54e50b7a7ce
1/* 2 * Copyright (C) 2006 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 17package com.android.internal.telephony.gsm; 18 19import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; 20import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; 21import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; 22import android.content.Context; 23import android.os.AsyncResult; 24import android.os.Message; 25import android.os.SystemProperties; 26import android.util.Log; 27 28import com.android.internal.telephony.AdnRecord; 29import com.android.internal.telephony.AdnRecordCache; 30import com.android.internal.telephony.AdnRecordLoader; 31import com.android.internal.telephony.CommandsInterface; 32import com.android.internal.telephony.IccFileHandler; 33import com.android.internal.telephony.IccRecords; 34import com.android.internal.telephony.IccUtils; 35import com.android.internal.telephony.IccVmFixedException; 36import com.android.internal.telephony.IccVmNotSupportedException; 37import com.android.internal.telephony.MccTable; 38 39import java.util.ArrayList; 40 41 42/** 43 * {@hide} 44 */ 45public final class SIMRecords extends IccRecords { 46 static final String LOG_TAG = "GSM"; 47 48 private static final boolean CRASH_RIL = false; 49 50 private static final boolean DBG = true; 51 52 // ***** Instance Variables 53 54 VoiceMailConstants mVmConfig; 55 56 57 SpnOverride mSpnOverride; 58 59 // ***** Cached SIM State; cleared on channel close 60 61 String imsi; 62 boolean callForwardingEnabled; 63 64 65 /** 66 * States only used by getSpnFsm FSM 67 */ 68 private Get_Spn_Fsm_State spnState; 69 70 /** CPHS service information (See CPHS 4.2 B.3.1.1) 71 * It will be set in onSimReady if reading GET_CPHS_INFO successfully 72 * mCphsInfo[0] is CPHS Phase 73 * mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table 74 */ 75 private byte[] mCphsInfo = null; 76 77 byte[] efMWIS = null; 78 byte[] efCPHS_MWI =null; 79 byte[] mEfCff = null; 80 byte[] mEfCfis = null; 81 82 83 int spnDisplayCondition; 84 // Numeric network codes listed in TS 51.011 EF[SPDI] 85 ArrayList<String> spdiNetworks = null; 86 87 String pnnHomeName = null; 88 89 // ***** Constants 90 91 // Bitmasks for SPN display rules. 92 static final int SPN_RULE_SHOW_SPN = 0x01; 93 static final int SPN_RULE_SHOW_PLMN = 0x02; 94 95 // From TS 51.011 EF[SPDI] section 96 static final int TAG_SPDI = 0xA3; 97 static final int TAG_SPDI_PLMN_LIST = 0x80; 98 99 // Full Name IEI from TS 24.008 100 static final int TAG_FULL_NETWORK_NAME = 0x43; 101 102 // Short Name IEI from TS 24.008 103 static final int TAG_SHORT_NETWORK_NAME = 0x45; 104 105 // active CFF from CPHS 4.2 B.4.5 106 static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a; 107 static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05; 108 static final int CFF_LINE1_MASK = 0x0f; 109 static final int CFF_LINE1_RESET = 0xf0; 110 111 // CPHS Service Table (See CPHS 4.2 B.3.1) 112 private static final int CPHS_SST_MBN_MASK = 0x30; 113 private static final int CPHS_SST_MBN_ENABLED = 0x30; 114 115 // ***** Event Constants 116 117 private static final int EVENT_SIM_READY = 1; 118 private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2; 119 private static final int EVENT_GET_IMSI_DONE = 3; 120 private static final int EVENT_GET_ICCID_DONE = 4; 121 private static final int EVENT_GET_MBI_DONE = 5; 122 private static final int EVENT_GET_MBDN_DONE = 6; 123 private static final int EVENT_GET_MWIS_DONE = 7; 124 private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8; 125 private static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM 126 private static final int EVENT_GET_MSISDN_DONE = 10; 127 private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11; 128 private static final int EVENT_GET_SPN_DONE = 12; 129 private static final int EVENT_GET_SPDI_DONE = 13; 130 private static final int EVENT_UPDATE_DONE = 14; 131 private static final int EVENT_GET_PNN_DONE = 15; 132 private static final int EVENT_GET_SST_DONE = 17; 133 private static final int EVENT_GET_ALL_SMS_DONE = 18; 134 private static final int EVENT_MARK_SMS_READ_DONE = 19; 135 private static final int EVENT_SET_MBDN_DONE = 20; 136 private static final int EVENT_SMS_ON_SIM = 21; 137 private static final int EVENT_GET_SMS_DONE = 22; 138 private static final int EVENT_GET_CFF_DONE = 24; 139 private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25; 140 private static final int EVENT_GET_INFO_CPHS_DONE = 26; 141 private static final int EVENT_SET_MSISDN_DONE = 30; 142 private static final int EVENT_SIM_REFRESH = 31; 143 private static final int EVENT_GET_CFIS_DONE = 32; 144 145 // ***** Constructor 146 147 SIMRecords(GSMPhone p) { 148 super(p); 149 150 adnCache = new AdnRecordCache(phone); 151 152 mVmConfig = new VoiceMailConstants(); 153 mSpnOverride = new SpnOverride(); 154 155 recordsRequested = false; // No load request is made till SIM ready 156 157 // recordsToLoad is set to 0 because no requests are made yet 158 recordsToLoad = 0; 159 160 161 p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null); 162 p.mCM.registerForOffOrNotAvailable( 163 this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); 164 p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null); 165 p.mCM.setOnIccRefresh(this, EVENT_SIM_REFRESH, null); 166 167 // Start off by setting empty state 168 onRadioOffOrNotAvailable(); 169 170 } 171 172 public void dispose() { 173 //Unregister for all events 174 phone.mCM.unregisterForSIMReady(this); 175 phone.mCM.unregisterForOffOrNotAvailable( this); 176 phone.mCM.unSetOnIccRefresh(this); 177 } 178 179 protected void finalize() { 180 if(DBG) Log.d(LOG_TAG, "SIMRecords finalized"); 181 } 182 183 protected void onRadioOffOrNotAvailable() { 184 imsi = null; 185 msisdn = null; 186 voiceMailNum = null; 187 countVoiceMessages = 0; 188 mncLength = UNINITIALIZED; 189 iccid = null; 190 // -1 means no EF_SPN found; treat accordingly. 191 spnDisplayCondition = -1; 192 efMWIS = null; 193 efCPHS_MWI = null; 194 spdiNetworks = null; 195 pnnHomeName = null; 196 197 adnCache.reset(); 198 199 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null); 200 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, null); 201 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null); 202 203 // recordsRequested is set to false indicating that the SIM 204 // read requests made so far are not valid. This is set to 205 // true only when fresh set of read requests are made. 206 recordsRequested = false; 207 } 208 209 210 //***** Public Methods 211 212 /** Returns null if SIM is not yet ready */ 213 public String getIMSI() { 214 return imsi; 215 } 216 217 public String getMsisdnNumber() { 218 return msisdn; 219 } 220 221 /** 222 * Set subscriber number to SIM record 223 * 224 * The subscriber number is stored in EF_MSISDN (TS 51.011) 225 * 226 * When the operation is complete, onComplete will be sent to its handler 227 * 228 * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters) 229 * @param number dailing nubmer (up to 20 digits) 230 * if the number starts with '+', then set to international TOA 231 * @param onComplete 232 * onComplete.obj will be an AsyncResult 233 * ((AsyncResult)onComplete.obj).exception == null on success 234 * ((AsyncResult)onComplete.obj).exception != null on fail 235 */ 236 public void setMsisdnNumber(String alphaTag, String number, 237 Message onComplete) { 238 239 msisdn = number; 240 msisdnTag = alphaTag; 241 242 if(DBG) log("Set MSISDN: " + msisdnTag +" " + msisdn); 243 244 245 AdnRecord adn = new AdnRecord(msisdnTag, msisdn); 246 247 new AdnRecordLoader(phone).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null, 248 obtainMessage(EVENT_SET_MSISDN_DONE, onComplete)); 249 } 250 251 public String getMsisdnAlphaTag() { 252 return msisdnTag; 253 } 254 255 public String getVoiceMailNumber() { 256 return voiceMailNum; 257 } 258 259 /** 260 * Set voice mail number to SIM record 261 * 262 * The voice mail number can be stored either in EF_MBDN (TS 51.011) or 263 * EF_MAILBOX_CPHS (CPHS 4.2) 264 * 265 * If EF_MBDN is available, store the voice mail number to EF_MBDN 266 * 267 * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS 268 * 269 * So the voice mail number will be stored in both EFs if both are available 270 * 271 * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail. 272 * 273 * When the operation is complete, onComplete will be sent to its handler 274 * 275 * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters) 276 * @param voiceNumber dailing nubmer (upto 20 digits) 277 * if the number is start with '+', then set to international TOA 278 * @param onComplete 279 * onComplete.obj will be an AsyncResult 280 * ((AsyncResult)onComplete.obj).exception == null on success 281 * ((AsyncResult)onComplete.obj).exception != null on fail 282 */ 283 public void setVoiceMailNumber(String alphaTag, String voiceNumber, 284 Message onComplete) { 285 if (isVoiceMailFixed) { 286 AsyncResult.forMessage((onComplete)).exception = 287 new IccVmFixedException("Voicemail number is fixed by operator"); 288 onComplete.sendToTarget(); 289 return; 290 } 291 292 newVoiceMailNum = voiceNumber; 293 newVoiceMailTag = alphaTag; 294 295 AdnRecord adn = new AdnRecord(newVoiceMailTag, newVoiceMailNum); 296 297 if (mailboxIndex != 0 && mailboxIndex != 0xff) { 298 299 new AdnRecordLoader(phone).updateEF(adn, EF_MBDN, EF_EXT6, 300 mailboxIndex, null, 301 obtainMessage(EVENT_SET_MBDN_DONE, onComplete)); 302 303 } else if (isCphsMailboxEnabled()) { 304 305 new AdnRecordLoader(phone).updateEF(adn, EF_MAILBOX_CPHS, 306 EF_EXT1, 1, null, 307 obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete)); 308 309 } else { 310 AsyncResult.forMessage((onComplete)).exception = 311 new IccVmNotSupportedException("Update SIM voice mailbox error"); 312 onComplete.sendToTarget(); 313 } 314 } 315 316 public String getVoiceMailAlphaTag() 317 { 318 return voiceMailTag; 319 } 320 321 /** 322 * Sets the SIM voice message waiting indicator records 323 * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported 324 * @param countWaiting The number of messages waiting, if known. Use 325 * -1 to indicate that an unknown number of 326 * messages are waiting 327 */ 328 public void 329 setVoiceMessageWaiting(int line, int countWaiting) { 330 if (line != 1) { 331 // only profile 1 is supported 332 return; 333 } 334 335 // range check 336 if (countWaiting < 0) { 337 countWaiting = -1; 338 } else if (countWaiting > 0xff) { 339 // TS 23.040 9.2.3.24.2 340 // "The value 255 shall be taken to mean 255 or greater" 341 countWaiting = 0xff; 342 } 343 344 countVoiceMessages = countWaiting; 345 346 ((GSMPhone) phone).notifyMessageWaitingIndicator(); 347 348 try { 349 if (efMWIS != null) { 350 // TS 51.011 10.3.45 351 352 // lsb of byte 0 is 'voicemail' status 353 efMWIS[0] = (byte)((efMWIS[0] & 0xfe) 354 | (countVoiceMessages == 0 ? 0 : 1)); 355 356 // byte 1 is the number of voice messages waiting 357 if (countWaiting < 0) { 358 // The spec does not define what this should be 359 // if we don't know the count 360 efMWIS[1] = 0; 361 } else { 362 efMWIS[1] = (byte) countWaiting; 363 } 364 365 phone.getIccFileHandler().updateEFLinearFixed( 366 EF_MWIS, 1, efMWIS, null, 367 obtainMessage (EVENT_UPDATE_DONE, EF_MWIS)); 368 } 369 370 if (efCPHS_MWI != null) { 371 // Refer CPHS4_2.WW6 B4.2.3 372 efCPHS_MWI[0] = (byte)((efCPHS_MWI[0] & 0xf0) 373 | (countVoiceMessages == 0 ? 0x5 : 0xa)); 374 375 phone.getIccFileHandler().updateEFTransparent( 376 EF_VOICE_MAIL_INDICATOR_CPHS, efCPHS_MWI, 377 obtainMessage (EVENT_UPDATE_DONE, EF_VOICE_MAIL_INDICATOR_CPHS)); 378 } 379 } catch (ArrayIndexOutOfBoundsException ex) { 380 Log.w(LOG_TAG, 381 "Error saving voice mail state to SIM. Probably malformed SIM record", ex); 382 } 383 } 384 385 public boolean getVoiceCallForwardingFlag() { 386 return callForwardingEnabled; 387 } 388 389 public void setVoiceCallForwardingFlag(int line, boolean enable) { 390 391 if (line != 1) return; // only line 1 is supported 392 393 callForwardingEnabled = enable; 394 395 ((GSMPhone) phone).notifyCallForwardingIndicator(); 396 397 try { 398 if (mEfCfis != null) { 399 // lsb is of byte 1 is voice status 400 if (enable) { 401 mEfCfis[1] |= 1; 402 } else { 403 mEfCfis[1] &= 0xfe; 404 } 405 406 // TODO: Should really update other fields in EF_CFIS, eg, 407 // dialing number. We don't read or use it right now. 408 409 phone.getIccFileHandler().updateEFLinearFixed( 410 EF_CFIS, 1, mEfCfis, null, 411 obtainMessage (EVENT_UPDATE_DONE, EF_CFIS)); 412 } 413 414 if (mEfCff != null) { 415 if (enable) { 416 mEfCff[0] = (byte) ((mEfCff[0] & CFF_LINE1_RESET) 417 | CFF_UNCONDITIONAL_ACTIVE); 418 } else { 419 mEfCff[0] = (byte) ((mEfCff[0] & CFF_LINE1_RESET) 420 | CFF_UNCONDITIONAL_DEACTIVE); 421 } 422 423 phone.getIccFileHandler().updateEFTransparent( 424 EF_CFF_CPHS, mEfCff, 425 obtainMessage (EVENT_UPDATE_DONE, EF_CFF_CPHS)); 426 } 427 } catch (ArrayIndexOutOfBoundsException ex) { 428 Log.w(LOG_TAG, 429 "Error saving call fowarding flag to SIM. " 430 + "Probably malformed SIM record", ex); 431 432 } 433 } 434 435 /** 436 * Called by STK Service when REFRESH is received. 437 * @param fileChanged indicates whether any files changed 438 * @param fileList if non-null, a list of EF files that changed 439 */ 440 public void onRefresh(boolean fileChanged, int[] fileList) { 441 if (fileChanged) { 442 // A future optimization would be to inspect fileList and 443 // only reload those files that we care about. For now, 444 // just re-fetch all SIM records that we cache. 445 fetchSimRecords(); 446 } 447 } 448 449 /** Returns the 5 or 6 digit MCC/MNC of the operator that 450 * provided the SIM card. Returns null of SIM is not yet ready 451 */ 452 String getSIMOperatorNumeric() { 453 if (imsi == null || mncLength == UNINITIALIZED || mncLength == UNKNOWN) { 454 return null; 455 } 456 457 // Length = length of MCC + length of MNC 458 // length of mcc = 3 (TS 23.003 Section 2.2) 459 return imsi.substring(0, 3 + mncLength); 460 } 461 462 // ***** Overridden from Handler 463 public void handleMessage(Message msg) { 464 AsyncResult ar; 465 AdnRecord adn; 466 467 byte data[]; 468 469 boolean isRecordLoadResponse = false; 470 471 try { switch (msg.what) { 472 case EVENT_SIM_READY: 473 onSimReady(); 474 break; 475 476 case EVENT_RADIO_OFF_OR_NOT_AVAILABLE: 477 onRadioOffOrNotAvailable(); 478 break; 479 480 /* IO events */ 481 case EVENT_GET_IMSI_DONE: 482 isRecordLoadResponse = true; 483 484 ar = (AsyncResult)msg.obj; 485 486 if (ar.exception != null) { 487 Log.e(LOG_TAG, "Exception querying IMSI, Exception:" + ar.exception); 488 break; 489 } 490 491 imsi = (String) ar.result; 492 493 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more 494 // than 15 (and usually 15). 495 if (imsi != null && (imsi.length() < 6 || imsi.length() > 15)) { 496 Log.e(LOG_TAG, "invalid IMSI " + imsi); 497 imsi = null; 498 } 499 500 Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxxxx"); 501 502 if (mncLength == UNKNOWN) { 503 // the SIM has told us all it knows, but it didn't know the mnc length. 504 // guess using the mcc 505 try { 506 int mcc = Integer.parseInt(imsi.substring(0,3)); 507 mncLength = MccTable.smallestDigitsMccForMnc(mcc); 508 } catch (NumberFormatException e) { 509 mncLength = UNKNOWN; 510 Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!"); 511 } 512 } 513 514 if (mncLength != UNKNOWN && mncLength != UNINITIALIZED) { 515 // finally have both the imsi and the mncLength and can parse the imsi properly 516 MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength)); 517 } 518 ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent( 519 SimCard.INTENT_VALUE_ICC_IMSI, null); 520 break; 521 522 case EVENT_GET_MBI_DONE: 523 boolean isValidMbdn; 524 isRecordLoadResponse = true; 525 526 ar = (AsyncResult)msg.obj; 527 data = (byte[]) ar.result; 528 529 isValidMbdn = false; 530 if (ar.exception == null) { 531 // Refer TS 51.011 Section 10.3.44 for content details 532 Log.d(LOG_TAG, "EF_MBI: " + 533 IccUtils.bytesToHexString(data)); 534 535 // Voice mail record number stored first 536 mailboxIndex = (int)data[0] & 0xff; 537 538 // check if dailing numbe id valid 539 if (mailboxIndex != 0 && mailboxIndex != 0xff) { 540 Log.d(LOG_TAG, "Got valid mailbox number for MBDN"); 541 isValidMbdn = true; 542 } 543 } 544 545 // one more record to load 546 recordsToLoad += 1; 547 548 if (isValidMbdn) { 549 // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED 550 new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6, 551 mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE)); 552 } else { 553 // If this EF not present, try mailbox as in CPHS standard 554 // CPHS (CPHS4_2.WW6) is a european standard. 555 new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, 556 EF_EXT1, 1, 557 obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 558 } 559 560 break; 561 case EVENT_GET_CPHS_MAILBOX_DONE: 562 case EVENT_GET_MBDN_DONE: 563 isRecordLoadResponse = true; 564 565 ar = (AsyncResult)msg.obj; 566 567 if (ar.exception != null) { 568 569 Log.d(LOG_TAG, "Invalid or missing EF" 570 + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]")); 571 572 // Bug #645770 fall back to CPHS 573 // FIXME should use SST to decide 574 575 if (msg.what == EVENT_GET_MBDN_DONE) { 576 //load CPHS on fail... 577 // FIXME right now, only load line1's CPHS voice mail entry 578 579 recordsToLoad += 1; 580 new AdnRecordLoader(phone).loadFromEF( 581 EF_MAILBOX_CPHS, EF_EXT1, 1, 582 obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 583 } 584 break; 585 } 586 587 adn = (AdnRecord)ar.result; 588 589 Log.d(LOG_TAG, "VM: " + adn + 590 ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]")); 591 592 if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) { 593 // Bug #645770 fall back to CPHS 594 // FIXME should use SST to decide 595 // FIXME right now, only load line1's CPHS voice mail entry 596 recordsToLoad += 1; 597 new AdnRecordLoader(phone).loadFromEF( 598 EF_MAILBOX_CPHS, EF_EXT1, 1, 599 obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 600 601 break; 602 } 603 604 voiceMailNum = adn.getNumber(); 605 voiceMailTag = adn.getAlphaTag(); 606 break; 607 608 case EVENT_GET_MSISDN_DONE: 609 isRecordLoadResponse = true; 610 611 ar = (AsyncResult)msg.obj; 612 613 if (ar.exception != null) { 614 Log.d(LOG_TAG, "Invalid or missing EF[MSISDN]"); 615 break; 616 } 617 618 adn = (AdnRecord)ar.result; 619 620 msisdn = adn.getNumber(); 621 msisdnTag = adn.getAlphaTag(); 622 623 Log.d(LOG_TAG, "MSISDN: " + msisdn); 624 break; 625 626 case EVENT_SET_MSISDN_DONE: 627 isRecordLoadResponse = false; 628 ar = (AsyncResult)msg.obj; 629 630 if (ar.userObj != null) { 631 AsyncResult.forMessage(((Message) ar.userObj)).exception 632 = ar.exception; 633 ((Message) ar.userObj).sendToTarget(); 634 } 635 break; 636 637 case EVENT_GET_MWIS_DONE: 638 isRecordLoadResponse = true; 639 640 ar = (AsyncResult)msg.obj; 641 data = (byte[])ar.result; 642 643 if (ar.exception != null) { 644 break; 645 } 646 647 Log.d(LOG_TAG, "EF_MWIS: " + 648 IccUtils.bytesToHexString(data)); 649 650 efMWIS = data; 651 652 if ((data[0] & 0xff) == 0xff) { 653 Log.d(LOG_TAG, "SIMRecords: Uninitialized record MWIS"); 654 break; 655 } 656 657 // Refer TS 51.011 Section 10.3.45 for the content description 658 boolean voiceMailWaiting = ((data[0] & 0x01) != 0); 659 countVoiceMessages = data[1] & 0xff; 660 661 if (voiceMailWaiting && countVoiceMessages == 0) { 662 // Unknown count = -1 663 countVoiceMessages = -1; 664 } 665 666 ((GSMPhone) phone).notifyMessageWaitingIndicator(); 667 break; 668 669 case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE: 670 isRecordLoadResponse = true; 671 672 ar = (AsyncResult)msg.obj; 673 data = (byte[])ar.result; 674 675 if (ar.exception != null) { 676 break; 677 } 678 679 efCPHS_MWI = data; 680 681 // Use this data if the EF[MWIS] exists and 682 // has been loaded 683 684 if (efMWIS == null) { 685 int indicator = (int)(data[0] & 0xf); 686 687 // Refer CPHS4_2.WW6 B4.2.3 688 if (indicator == 0xA) { 689 // Unknown count = -1 690 countVoiceMessages = -1; 691 } else if (indicator == 0x5) { 692 countVoiceMessages = 0; 693 } 694 695 ((GSMPhone) phone).notifyMessageWaitingIndicator(); 696 } 697 break; 698 699 case EVENT_GET_ICCID_DONE: 700 isRecordLoadResponse = true; 701 702 ar = (AsyncResult)msg.obj; 703 data = (byte[])ar.result; 704 705 if (ar.exception != null) { 706 break; 707 } 708 709 iccid = IccUtils.bcdToString(data, 0, data.length); 710 711 Log.d(LOG_TAG, "iccid: " + iccid); 712 713 break; 714 715 716 case EVENT_GET_AD_DONE: 717 try { 718 isRecordLoadResponse = true; 719 720 ar = (AsyncResult)msg.obj; 721 data = (byte[])ar.result; 722 723 if (ar.exception != null) { 724 break; 725 } 726 727 Log.d(LOG_TAG, "EF_AD: " + 728 IccUtils.bytesToHexString(data)); 729 730 if (data.length < 3) { 731 Log.d(LOG_TAG, "SIMRecords: Corrupt AD data on SIM"); 732 break; 733 } 734 735 if (data.length == 3) { 736 Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD"); 737 break; 738 } 739 740 mncLength = (int)data[3] & 0xf; 741 742 if (mncLength == 0xf) { 743 mncLength = UNKNOWN; 744 } 745 } finally { 746 if (mncLength == UNKNOWN || mncLength == UNINITIALIZED) { 747 if (imsi != null) { 748 try { 749 int mcc = Integer.parseInt(imsi.substring(0,3)); 750 751 mncLength = MccTable.smallestDigitsMccForMnc(mcc); 752 } catch (NumberFormatException e) { 753 mncLength = UNKNOWN; 754 Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!"); 755 } 756 } else { 757 // Indicate we got this info, but it didn't contain the length. 758 mncLength = UNKNOWN; 759 760 Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD"); 761 } 762 } 763 if (imsi != null && mncLength != UNKNOWN) { 764 // finally have both imsi and the length of the mnc and can parse 765 // the imsi properly 766 MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength)); 767 } 768 } 769 break; 770 771 case EVENT_GET_SPN_DONE: 772 isRecordLoadResponse = true; 773 ar = (AsyncResult) msg.obj; 774 getSpnFsm(false, ar); 775 break; 776 777 case EVENT_GET_CFF_DONE: 778 isRecordLoadResponse = true; 779 780 ar = (AsyncResult) msg.obj; 781 data = (byte[]) ar.result; 782 783 if (ar.exception != null) { 784 break; 785 } 786 787 Log.d(LOG_TAG, "EF_CFF_CPHS: " + 788 IccUtils.bytesToHexString(data)); 789 mEfCff = data; 790 791 if (mEfCfis == null) { 792 callForwardingEnabled = 793 ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE); 794 795 ((GSMPhone) phone).notifyCallForwardingIndicator(); 796 } 797 break; 798 799 case EVENT_GET_SPDI_DONE: 800 isRecordLoadResponse = true; 801 802 ar = (AsyncResult)msg.obj; 803 data = (byte[])ar.result; 804 805 if (ar.exception != null) { 806 break; 807 } 808 809 parseEfSpdi(data); 810 break; 811 812 case EVENT_UPDATE_DONE: 813 ar = (AsyncResult)msg.obj; 814 if (ar.exception != null) { 815 Log.i(LOG_TAG, "SIMRecords update failed", ar.exception); 816 } 817 break; 818 819 case EVENT_GET_PNN_DONE: 820 isRecordLoadResponse = true; 821 822 ar = (AsyncResult)msg.obj; 823 data = (byte[])ar.result; 824 825 if (ar.exception != null) { 826 break; 827 } 828 829 SimTlv tlv = new SimTlv(data, 0, data.length); 830 831 for ( ; tlv.isValidObject() ; tlv.nextObject()) { 832 if (tlv.getTag() == TAG_FULL_NETWORK_NAME) { 833 pnnHomeName 834 = IccUtils.networkNameToString( 835 tlv.getData(), 0, tlv.getData().length); 836 break; 837 } 838 } 839 break; 840 841 case EVENT_GET_ALL_SMS_DONE: 842 isRecordLoadResponse = true; 843 844 ar = (AsyncResult)msg.obj; 845 if (ar.exception != null) 846 break; 847 848 handleSmses((ArrayList) ar.result); 849 break; 850 851 case EVENT_MARK_SMS_READ_DONE: 852 Log.i("ENF", "marked read: sms " + msg.arg1); 853 break; 854 855 856 case EVENT_SMS_ON_SIM: 857 isRecordLoadResponse = false; 858 859 ar = (AsyncResult)msg.obj; 860 861 int[] index = (int[])ar.result; 862 863 if (ar.exception != null || index.length != 1) { 864 Log.e(LOG_TAG, "[SIMRecords] Error on SMS_ON_SIM with exp " 865 + ar.exception + " length " + index.length); 866 } else { 867 Log.d(LOG_TAG, "READ EF_SMS RECORD index=" + index[0]); 868 phone.getIccFileHandler().loadEFLinearFixed(EF_SMS,index[0], 869 obtainMessage(EVENT_GET_SMS_DONE)); 870 } 871 break; 872 873 case EVENT_GET_SMS_DONE: 874 isRecordLoadResponse = false; 875 ar = (AsyncResult)msg.obj; 876 if (ar.exception == null) { 877 handleSms((byte[])ar.result); 878 } else { 879 Log.e(LOG_TAG, "[SIMRecords] Error on GET_SMS with exp " 880 + ar.exception); 881 } 882 break; 883 case EVENT_GET_SST_DONE: 884 isRecordLoadResponse = true; 885 886 ar = (AsyncResult)msg.obj; 887 data = (byte[])ar.result; 888 889 if (ar.exception != null) { 890 break; 891 } 892 893 //Log.d(LOG_TAG, "SST: " + IccUtils.bytesToHexString(data)); 894 break; 895 896 case EVENT_GET_INFO_CPHS_DONE: 897 isRecordLoadResponse = true; 898 899 ar = (AsyncResult)msg.obj; 900 901 if (ar.exception != null) { 902 break; 903 } 904 905 mCphsInfo = (byte[])ar.result; 906 907 if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo)); 908 break; 909 910 case EVENT_SET_MBDN_DONE: 911 isRecordLoadResponse = false; 912 ar = (AsyncResult)msg.obj; 913 914 if (ar.exception == null) { 915 voiceMailNum = newVoiceMailNum; 916 voiceMailTag = newVoiceMailTag; 917 } 918 919 if (isCphsMailboxEnabled()) { 920 adn = new AdnRecord(voiceMailTag, voiceMailNum); 921 Message onCphsCompleted = (Message) ar.userObj; 922 923 /* write to cphs mailbox whenever it is available but 924 * we only need notify caller once if both updating are 925 * successful. 926 * 927 * so if set_mbdn successful, notify caller here and set 928 * onCphsCompleted to null 929 */ 930 if (ar.exception == null && ar.userObj != null) { 931 AsyncResult.forMessage(((Message) ar.userObj)).exception 932 = null; 933 ((Message) ar.userObj).sendToTarget(); 934 935 if (DBG) log("Callback with MBDN successful."); 936 937 onCphsCompleted = null; 938 } 939 940 new AdnRecordLoader(phone). 941 updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null, 942 obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, 943 onCphsCompleted)); 944 } else { 945 if (ar.userObj != null) { 946 AsyncResult.forMessage(((Message) ar.userObj)).exception 947 = ar.exception; 948 ((Message) ar.userObj).sendToTarget(); 949 } 950 } 951 break; 952 case EVENT_SET_CPHS_MAILBOX_DONE: 953 isRecordLoadResponse = false; 954 ar = (AsyncResult)msg.obj; 955 if(ar.exception == null) { 956 voiceMailNum = newVoiceMailNum; 957 voiceMailTag = newVoiceMailTag; 958 } else { 959 if (DBG) log("Set CPHS MailBox with exception: " 960 + ar.exception); 961 } 962 if (ar.userObj != null) { 963 if (DBG) log("Callback with CPHS MB successful."); 964 AsyncResult.forMessage(((Message) ar.userObj)).exception 965 = ar.exception; 966 ((Message) ar.userObj).sendToTarget(); 967 } 968 break; 969 case EVENT_SIM_REFRESH: 970 isRecordLoadResponse = false; 971 ar = (AsyncResult)msg.obj; 972 if (DBG) log("Sim REFRESH with exception: " + ar.exception); 973 if (ar.exception == null) { 974 handleSimRefresh((int[])(ar.result)); 975 } 976 break; 977 case EVENT_GET_CFIS_DONE: 978 isRecordLoadResponse = true; 979 980 ar = (AsyncResult)msg.obj; 981 data = (byte[])ar.result; 982 983 if (ar.exception != null) { 984 break; 985 } 986 987 Log.d(LOG_TAG, "EF_CFIS: " + 988 IccUtils.bytesToHexString(data)); 989 990 mEfCfis = data; 991 992 // Refer TS 51.011 Section 10.3.46 for the content description 993 callForwardingEnabled = ((data[1] & 0x01) != 0); 994 995 ((GSMPhone) phone).notifyCallForwardingIndicator(); 996 break; 997 998 }}catch (RuntimeException exc) { 999 // I don't want these exceptions to be fatal 1000 Log.w(LOG_TAG, "Exception parsing SIM record", exc); 1001 } finally { 1002 // Count up record load responses even if they are fails 1003 if (isRecordLoadResponse) { 1004 onRecordLoaded(); 1005 } 1006 } 1007 } 1008 1009 private void handleFileUpdate(int efid) { 1010 switch(efid) { 1011 case EF_MBDN: 1012 recordsToLoad++; 1013 new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6, 1014 mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE)); 1015 break; 1016 case EF_MAILBOX_CPHS: 1017 recordsToLoad++; 1018 new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1, 1019 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 1020 break; 1021 default: 1022 // For now, fetch all records if this is not a 1023 // voicemail number. 1024 // TODO: Handle other cases, instead of fetching all. 1025 adnCache.reset(); 1026 fetchSimRecords(); 1027 break; 1028 } 1029 } 1030 1031 private void handleSimRefresh(int[] result) { 1032 if (result == null || result.length == 0) { 1033 if (DBG) log("handleSimRefresh without input"); 1034 return; 1035 } 1036 1037 switch ((result[0])) { 1038 case CommandsInterface.SIM_REFRESH_FILE_UPDATED: 1039 if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED"); 1040 // result[1] contains the EFID of the updated file. 1041 int efid = result[1]; 1042 handleFileUpdate(efid); 1043 break; 1044 case CommandsInterface.SIM_REFRESH_INIT: 1045 if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT"); 1046 // need to reload all files (that we care about) 1047 adnCache.reset(); 1048 fetchSimRecords(); 1049 break; 1050 case CommandsInterface.SIM_REFRESH_RESET: 1051 if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET"); 1052 phone.mCM.setRadioPower(false, null); 1053 /* Note: no need to call setRadioPower(true). Assuming the desired 1054 * radio power state is still ON (as tracked by ServiceStateTracker), 1055 * ServiceStateTracker will call setRadioPower when it receives the 1056 * RADIO_STATE_CHANGED notification for the power off. And if the 1057 * desired power state has changed in the interim, we don't want to 1058 * override it with an unconditional power on. 1059 */ 1060 break; 1061 default: 1062 // unknown refresh operation 1063 if (DBG) log("handleSimRefresh with unknown operation"); 1064 break; 1065 } 1066 } 1067 1068 private void handleSms(byte[] ba) { 1069 if (ba[0] != 0) 1070 Log.d("ENF", "status : " + ba[0]); 1071 1072 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1073 // 3 == "received by MS from network; message to be read" 1074 if (ba[0] == 3) { 1075 int n = ba.length; 1076 1077 // Note: Data may include trailing FF's. That's OK; message 1078 // should still parse correctly. 1079 byte[] pdu = new byte[n - 1]; 1080 System.arraycopy(ba, 1, pdu, 0, n - 1); 1081 SmsMessage message = SmsMessage.createFromPdu(pdu); 1082 1083 ((GSMPhone) phone).mSMS.dispatchMessage(message); 1084 } 1085 } 1086 1087 1088 private void handleSmses(ArrayList messages) { 1089 int count = messages.size(); 1090 1091 for (int i = 0; i < count; i++) { 1092 byte[] ba = (byte[]) messages.get(i); 1093 1094 if (ba[0] != 0) 1095 Log.i("ENF", "status " + i + ": " + ba[0]); 1096 1097 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1098 // 3 == "received by MS from network; message to be read" 1099 1100 if (ba[0] == 3) { 1101 int n = ba.length; 1102 1103 // Note: Data may include trailing FF's. That's OK; message 1104 // should still parse correctly. 1105 byte[] pdu = new byte[n - 1]; 1106 System.arraycopy(ba, 1, pdu, 0, n - 1); 1107 SmsMessage message = SmsMessage.createFromPdu(pdu); 1108 1109 ((GSMPhone) phone).mSMS.dispatchMessage(message); 1110 1111 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1112 // 1 == "received by MS from network; message read" 1113 1114 ba[0] = 1; 1115 1116 if (false) { // XXX writing seems to crash RdoServD 1117 phone.getIccFileHandler().updateEFLinearFixed(EF_SMS, 1118 i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i)); 1119 } 1120 } 1121 } 1122 } 1123 1124 protected void onRecordLoaded() { 1125 // One record loaded successfully or failed, In either case 1126 // we need to update the recordsToLoad count 1127 recordsToLoad -= 1; 1128 1129 if (recordsToLoad == 0 && recordsRequested == true) { 1130 onAllRecordsLoaded(); 1131 } else if (recordsToLoad < 0) { 1132 Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected"); 1133 recordsToLoad = 0; 1134 } 1135 } 1136 1137 protected void onAllRecordsLoaded() { 1138 Log.d(LOG_TAG, "SIMRecords: record load complete"); 1139 1140 String operator = getSIMOperatorNumeric(); 1141 1142 // Some fields require more than one SIM record to set 1143 1144 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator); 1145 1146 if (imsi != null) { 1147 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, 1148 MccTable.countryCodeForMcc(Integer.parseInt(imsi.substring(0,3)))); 1149 } 1150 else { 1151 Log.e("SIM", "[SIMRecords] onAllRecordsLoaded: imsi is NULL!"); 1152 } 1153 1154 setVoiceMailByCountry(operator); 1155 setSpnFromConfig(operator); 1156 1157 recordsLoadedRegistrants.notifyRegistrants( 1158 new AsyncResult(null, null, null)); 1159 ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent( 1160 SimCard.INTENT_VALUE_ICC_LOADED, null); 1161 } 1162 1163 //***** Private methods 1164 1165 private void setSpnFromConfig(String carrier) { 1166 if (mSpnOverride.containsCarrier(carrier)) { 1167 spn = mSpnOverride.getSpn(carrier); 1168 } 1169 } 1170 1171 1172 private void setVoiceMailByCountry (String spn) { 1173 if (mVmConfig.containsCarrier(spn)) { 1174 isVoiceMailFixed = true; 1175 voiceMailNum = mVmConfig.getVoiceMailNumber(spn); 1176 voiceMailTag = mVmConfig.getVoiceMailTag(spn); 1177 } 1178 } 1179 1180 private void onSimReady() { 1181 /* broadcast intent SIM_READY here so that we can make sure 1182 READY is sent before IMSI ready 1183 */ 1184 ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent( 1185 SimCard.INTENT_VALUE_ICC_READY, null); 1186 1187 fetchSimRecords(); 1188 } 1189 1190 private void fetchSimRecords() { 1191 recordsRequested = true; 1192 IccFileHandler iccFh = phone.getIccFileHandler(); 1193 1194 Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad); 1195 1196 phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE)); 1197 recordsToLoad++; 1198 1199 iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); 1200 recordsToLoad++; 1201 1202 // FIXME should examine EF[MSISDN]'s capability configuration 1203 // to determine which is the voice/data/fax line 1204 new AdnRecordLoader(phone).loadFromEF(EF_MSISDN, EF_EXT1, 1, 1205 obtainMessage(EVENT_GET_MSISDN_DONE)); 1206 recordsToLoad++; 1207 1208 // Record number is subscriber profile 1209 iccFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE)); 1210 recordsToLoad++; 1211 1212 iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE)); 1213 recordsToLoad++; 1214 1215 // Record number is subscriber profile 1216 iccFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE)); 1217 recordsToLoad++; 1218 1219 1220 // Also load CPHS-style voice mail indicator, which stores 1221 // the same info as EF[MWIS]. If both exist, both are updated 1222 // but the EF[MWIS] data is preferred 1223 // Please note this must be loaded after EF[MWIS] 1224 iccFh.loadEFTransparent( 1225 EF_VOICE_MAIL_INDICATOR_CPHS, 1226 obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE)); 1227 recordsToLoad++; 1228 1229 // Same goes for Call Forward Status indicator: fetch both 1230 // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred. 1231 iccFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE)); 1232 recordsToLoad++; 1233 iccFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE)); 1234 recordsToLoad++; 1235 1236 1237 getSpnFsm(true, null); 1238 1239 iccFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE)); 1240 recordsToLoad++; 1241 1242 iccFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE)); 1243 recordsToLoad++; 1244 1245 iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE)); 1246 recordsToLoad++; 1247 1248 iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE)); 1249 recordsToLoad++; 1250 1251 // XXX should seek instead of examining them all 1252 if (false) { // XXX 1253 iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE)); 1254 recordsToLoad++; 1255 } 1256 1257 if (CRASH_RIL) { 1258 String sms = "0107912160130310f20404d0110041007030208054832b0120" 1259 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1260 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1261 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1262 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1263 + "ffffffffffffffffffffffffffffff"; 1264 byte[] ba = IccUtils.hexStringToBytes(sms); 1265 1266 iccFh.updateEFLinearFixed(EF_SMS, 1, ba, null, 1267 obtainMessage(EVENT_MARK_SMS_READ_DONE, 1)); 1268 } 1269 } 1270 1271 /** 1272 * Returns the SpnDisplayRule based on settings on the SIM and the 1273 * specified plmn (currently-registered PLMN). See TS 22.101 Annex A 1274 * and TS 51.011 10.3.11 for details. 1275 * 1276 * If the SPN is not found on the SIM, the rule is always PLMN_ONLY. 1277 */ 1278 protected int getDisplayRule(String plmn) { 1279 int rule; 1280 if (spn == null || spnDisplayCondition == -1) { 1281 // EF_SPN was not found on the SIM, or not yet loaded. Just show ONS. 1282 rule = SPN_RULE_SHOW_PLMN; 1283 } else if (isOnMatchingPlmn(plmn)) { 1284 rule = SPN_RULE_SHOW_SPN; 1285 if ((spnDisplayCondition & 0x01) == 0x01) { 1286 // ONS required when registered to HPLMN or PLMN in EF_SPDI 1287 rule |= SPN_RULE_SHOW_PLMN; 1288 } 1289 } else { 1290 rule = SPN_RULE_SHOW_PLMN; 1291 if ((spnDisplayCondition & 0x02) == 0x00) { 1292 // SPN required if not registered to HPLMN or PLMN in EF_SPDI 1293 rule |= SPN_RULE_SHOW_SPN; 1294 } 1295 } 1296 return rule; 1297 } 1298 1299 /** 1300 * Checks if plmn is HPLMN or on the spdiNetworks list. 1301 */ 1302 private boolean isOnMatchingPlmn(String plmn) { 1303 if (plmn == null) return false; 1304 1305 if (plmn.equals(getSIMOperatorNumeric())) { 1306 return true; 1307 } 1308 1309 if (spdiNetworks != null) { 1310 for (String spdiNet : spdiNetworks) { 1311 if (plmn.equals(spdiNet)) { 1312 return true; 1313 } 1314 } 1315 } 1316 return false; 1317 } 1318 1319 /** 1320 * States of Get SPN Finite State Machine which only used by getSpnFsm() 1321 */ 1322 private enum Get_Spn_Fsm_State { 1323 IDLE, // No initialized 1324 INIT, // Start FSM 1325 READ_SPN_3GPP, // Load EF_SPN firstly 1326 READ_SPN_CPHS, // Load EF_SPN_CPHS secondly 1327 READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last 1328 } 1329 1330 /** 1331 * Finite State Machine to load Service Provider Name , which can be stored 1332 * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2) 1333 * 1334 * After starting, FSM will search SPN EFs in order and stop after finding 1335 * the first valid SPN 1336 * 1337 * @param start set true only for initialize loading 1338 * @param ar the AsyncResult from loadEFTransparent 1339 * ar.exception holds exception in error 1340 * ar.result is byte[] for data in success 1341 */ 1342 private void getSpnFsm(boolean start, AsyncResult ar) { 1343 byte[] data; 1344 1345 if (start) { 1346 spnState = Get_Spn_Fsm_State.INIT; 1347 } 1348 1349 switch(spnState){ 1350 case INIT: 1351 spn = null; 1352 1353 phone.getIccFileHandler().loadEFTransparent( EF_SPN, 1354 obtainMessage(EVENT_GET_SPN_DONE)); 1355 recordsToLoad++; 1356 1357 spnState = Get_Spn_Fsm_State.READ_SPN_3GPP; 1358 break; 1359 case READ_SPN_3GPP: 1360 if (ar != null && ar.exception == null) { 1361 data = (byte[]) ar.result; 1362 spnDisplayCondition = 0xff & data[0]; 1363 spn = IccUtils.adnStringFieldToString(data, 1, data.length - 1); 1364 1365 if (DBG) log("Load EF_SPN: " + spn 1366 + " spnDisplayCondition: " + spnDisplayCondition); 1367 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); 1368 1369 spnState = Get_Spn_Fsm_State.IDLE; 1370 } else { 1371 phone.getIccFileHandler().loadEFTransparent( EF_SPN_CPHS, 1372 obtainMessage(EVENT_GET_SPN_DONE)); 1373 recordsToLoad++; 1374 1375 spnState = Get_Spn_Fsm_State.READ_SPN_CPHS; 1376 1377 // See TS 51.011 10.3.11. Basically, default to 1378 // show PLMN always, and SPN also if roaming. 1379 spnDisplayCondition = -1; 1380 } 1381 break; 1382 case READ_SPN_CPHS: 1383 if (ar != null && ar.exception == null) { 1384 data = (byte[]) ar.result; 1385 spn = IccUtils.adnStringFieldToString( 1386 data, 0, data.length - 1 ); 1387 1388 if (DBG) log("Load EF_SPN_CPHS: " + spn); 1389 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); 1390 1391 spnState = Get_Spn_Fsm_State.IDLE; 1392 } else { 1393 phone.getIccFileHandler().loadEFTransparent( 1394 EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE)); 1395 recordsToLoad++; 1396 1397 spnState = Get_Spn_Fsm_State.READ_SPN_SHORT_CPHS; 1398 } 1399 break; 1400 case READ_SPN_SHORT_CPHS: 1401 if (ar != null && ar.exception == null) { 1402 data = (byte[]) ar.result; 1403 spn = IccUtils.adnStringFieldToString( 1404 data, 0, data.length - 1); 1405 1406 if (DBG) log("Load EF_SPN_SHORT_CPHS: " + spn); 1407 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); 1408 }else { 1409 if (DBG) log("No SPN loaded in either CHPS or 3GPP"); 1410 } 1411 1412 spnState = Get_Spn_Fsm_State.IDLE; 1413 break; 1414 default: 1415 spnState = Get_Spn_Fsm_State.IDLE; 1416 } 1417 } 1418 1419 /** 1420 * Parse TS 51.011 EF[SPDI] record 1421 * This record contains the list of numeric network IDs that 1422 * are treated specially when determining SPN display 1423 */ 1424 private void 1425 parseEfSpdi(byte[] data) { 1426 SimTlv tlv = new SimTlv(data, 0, data.length); 1427 1428 byte[] plmnEntries = null; 1429 1430 for ( ; tlv.isValidObject() ; tlv.nextObject()) { 1431 // Skip SPDI tag, if existant 1432 if (tlv.getTag() == TAG_SPDI) { 1433 tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length); 1434 } 1435 // There should only be one TAG_SPDI_PLMN_LIST 1436 if (tlv.getTag() == TAG_SPDI_PLMN_LIST) { 1437 plmnEntries = tlv.getData(); 1438 break; 1439 } 1440 } 1441 1442 if (plmnEntries == null) { 1443 return; 1444 } 1445 1446 spdiNetworks = new ArrayList<String>(plmnEntries.length / 3); 1447 1448 for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) { 1449 String plmnCode; 1450 plmnCode = IccUtils.bcdToString(plmnEntries, i, 3); 1451 1452 // Valid operator codes are 5 or 6 digits 1453 if (plmnCode.length() >= 5) { 1454 log("EF_SPDI network: " + plmnCode); 1455 spdiNetworks.add(plmnCode); 1456 } 1457 } 1458 } 1459 1460 /** 1461 * check to see if Mailbox Number is allocated and activated in CPHS SST 1462 */ 1463 private boolean isCphsMailboxEnabled() { 1464 if (mCphsInfo == null) return false; 1465 return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED ); 1466 } 1467 1468 protected void log(String s) { 1469 Log.d(LOG_TAG, "[SIMRecords] " + s); 1470 } 1471 1472} 1473