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