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