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