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