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