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 17 package com.android.internal.telephony.gsm; 18 19 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; 20 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; 21 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; 22 import android.content.Context; 23 import android.os.AsyncResult; 24 import android.os.Message; 25 import android.os.SystemProperties; 26 import android.util.Log; 27 28 import com.android.internal.telephony.AdnRecord; 29 import com.android.internal.telephony.AdnRecordCache; 30 import com.android.internal.telephony.AdnRecordLoader; 31 import com.android.internal.telephony.BaseCommands; 32 import com.android.internal.telephony.CommandsInterface; 33 import com.android.internal.telephony.IccFileHandler; 34 import com.android.internal.telephony.IccRecords; 35 import com.android.internal.telephony.IccUtils; 36 import com.android.internal.telephony.IccVmFixedException; 37 import com.android.internal.telephony.IccVmNotSupportedException; 38 import com.android.internal.telephony.MccTable; 39 import com.android.internal.telephony.Phone; 40 import com.android.internal.telephony.PhoneBase; 41 import com.android.internal.telephony.SmsMessageBase; 42 43 import java.util.ArrayList; 44 45 46 /** 47 * {@hide} 48 */ 49 public class SIMRecords extends IccRecords { 50 protected static final String LOG_TAG = "GSM"; 51 52 private static final boolean CRASH_RIL = false; 53 54 protected static final boolean DBG = true; 55 56 // ***** Instance Variables 57 58 VoiceMailConstants mVmConfig; 59 60 61 SpnOverride mSpnOverride; 62 63 // ***** Cached SIM State; cleared on channel close 64 65 private String imsi; 66 private boolean callForwardingEnabled; 67 68 69 /** 70 * States only used by getSpnFsm FSM 71 */ 72 private Get_Spn_Fsm_State spnState; 73 74 /** CPHS service information (See CPHS 4.2 B.3.1.1) 75 * It will be set in onSimReady if reading GET_CPHS_INFO successfully 76 * mCphsInfo[0] is CPHS Phase 77 * mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table 78 */ 79 private byte[] mCphsInfo = null; 80 boolean mCspPlmnEnabled = true; 81 82 byte[] efMWIS = null; 83 byte[] efCPHS_MWI =null; 84 byte[] mEfCff = null; 85 byte[] mEfCfis = null; 86 87 88 int spnDisplayCondition; 89 // Numeric network codes listed in TS 51.011 EF[SPDI] 90 ArrayList<String> spdiNetworks = null; 91 92 String pnnHomeName = null; 93 94 // ***** Constants 95 96 // Bitmasks for SPN display rules. 97 static final int SPN_RULE_SHOW_SPN = 0x01; 98 static final int SPN_RULE_SHOW_PLMN = 0x02; 99 100 // From TS 51.011 EF[SPDI] section 101 static final int TAG_SPDI = 0xA3; 102 static final int TAG_SPDI_PLMN_LIST = 0x80; 103 104 // Full Name IEI from TS 24.008 105 static final int TAG_FULL_NETWORK_NAME = 0x43; 106 107 // Short Name IEI from TS 24.008 108 static final int TAG_SHORT_NETWORK_NAME = 0x45; 109 110 // active CFF from CPHS 4.2 B.4.5 111 static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a; 112 static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05; 113 static final int CFF_LINE1_MASK = 0x0f; 114 static final int CFF_LINE1_RESET = 0xf0; 115 116 // CPHS Service Table (See CPHS 4.2 B.3.1) 117 private static final int CPHS_SST_MBN_MASK = 0x30; 118 private static final int CPHS_SST_MBN_ENABLED = 0x30; 119 120 // ***** Event Constants 121 122 private static final int EVENT_SIM_READY = 1; 123 private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2; 124 protected static final int EVENT_GET_IMSI_DONE = 3; 125 protected static final int EVENT_GET_ICCID_DONE = 4; 126 private static final int EVENT_GET_MBI_DONE = 5; 127 private static final int EVENT_GET_MBDN_DONE = 6; 128 private static final int EVENT_GET_MWIS_DONE = 7; 129 private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8; 130 protected static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM 131 protected static final int EVENT_GET_MSISDN_DONE = 10; 132 private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11; 133 private static final int EVENT_GET_SPN_DONE = 12; 134 private static final int EVENT_GET_SPDI_DONE = 13; 135 private static final int EVENT_UPDATE_DONE = 14; 136 private static final int EVENT_GET_PNN_DONE = 15; 137 private static final int EVENT_GET_SST_DONE = 17; 138 private static final int EVENT_GET_ALL_SMS_DONE = 18; 139 private static final int EVENT_MARK_SMS_READ_DONE = 19; 140 private static final int EVENT_SET_MBDN_DONE = 20; 141 private static final int EVENT_SMS_ON_SIM = 21; 142 private static final int EVENT_GET_SMS_DONE = 22; 143 private static final int EVENT_GET_CFF_DONE = 24; 144 private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25; 145 private static final int EVENT_GET_INFO_CPHS_DONE = 26; 146 private static final int EVENT_SET_MSISDN_DONE = 30; 147 private static final int EVENT_SIM_REFRESH = 31; 148 private static final int EVENT_GET_CFIS_DONE = 32; 149 private static final int EVENT_GET_CSP_CPHS_DONE = 33; 150 151 // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length. 152 153 private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = { 154 "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032", 155 "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040", 156 "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750", 157 "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800", 158 "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808", 159 "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816", 160 "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824", 161 "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832", 162 "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840", 163 "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848", 164 "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877", 165 "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885", 166 "405886", "405908", "405909", "405910", "405911", "405925", "405926", "405927", 167 "405928", "405929", "405932" 168 }; 169 170 // ***** Constructor 171 172 public SIMRecords(PhoneBase p) { 173 super(p); 174 175 adnCache = new AdnRecordCache(phone); 176 177 mVmConfig = new VoiceMailConstants(); 178 mSpnOverride = new SpnOverride(); 179 180 recordsRequested = false; // No load request is made till SIM ready 181 182 // recordsToLoad is set to 0 because no requests are made yet 183 recordsToLoad = 0; 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 default: 1086 super.handleMessage(msg); // IccRecords handles generic record load responses 1087 1088 }}catch (RuntimeException exc) { 1089 // I don't want these exceptions to be fatal 1090 Log.w(LOG_TAG, "Exception parsing SIM record", exc); 1091 } finally { 1092 // Count up record load responses even if they are fails 1093 if (isRecordLoadResponse) { 1094 onRecordLoaded(); 1095 } 1096 } 1097 } 1098 1099 private void handleFileUpdate(int efid) { 1100 switch(efid) { 1101 case EF_MBDN: 1102 recordsToLoad++; 1103 new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6, 1104 mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE)); 1105 break; 1106 case EF_MAILBOX_CPHS: 1107 recordsToLoad++; 1108 new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1, 1109 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 1110 break; 1111 case EF_CSP_CPHS: 1112 recordsToLoad++; 1113 Log.i(LOG_TAG, "[CSP] SIM Refresh for EF_CSP_CPHS"); 1114 phone.getIccFileHandler().loadEFTransparent(EF_CSP_CPHS, 1115 obtainMessage(EVENT_GET_CSP_CPHS_DONE)); 1116 break; 1117 default: 1118 // For now, fetch all records if this is not a 1119 // voicemail number. 1120 // TODO: Handle other cases, instead of fetching all. 1121 adnCache.reset(); 1122 fetchSimRecords(); 1123 break; 1124 } 1125 } 1126 1127 private void handleSimRefresh(int[] result) { 1128 if (result == null || result.length == 0) { 1129 if (DBG) log("handleSimRefresh without input"); 1130 return; 1131 } 1132 1133 switch ((result[0])) { 1134 case CommandsInterface.SIM_REFRESH_FILE_UPDATED: 1135 if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED"); 1136 // result[1] contains the EFID of the updated file. 1137 int efid = result[1]; 1138 handleFileUpdate(efid); 1139 break; 1140 case CommandsInterface.SIM_REFRESH_INIT: 1141 if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT"); 1142 // need to reload all files (that we care about) 1143 adnCache.reset(); 1144 fetchSimRecords(); 1145 break; 1146 case CommandsInterface.SIM_REFRESH_RESET: 1147 if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET"); 1148 phone.mCM.setRadioPower(false, null); 1149 /* Note: no need to call setRadioPower(true). Assuming the desired 1150 * radio power state is still ON (as tracked by ServiceStateTracker), 1151 * ServiceStateTracker will call setRadioPower when it receives the 1152 * RADIO_STATE_CHANGED notification for the power off. And if the 1153 * desired power state has changed in the interim, we don't want to 1154 * override it with an unconditional power on. 1155 */ 1156 break; 1157 default: 1158 // unknown refresh operation 1159 if (DBG) log("handleSimRefresh with unknown operation"); 1160 break; 1161 } 1162 } 1163 1164 /** 1165 * Dispatch 3GPP format message. Overridden for CDMA/LTE phones by 1166 * {@link com.android.internal.telephony.cdma.CdmaLteUiccRecords} 1167 * to send messages to the secondary 3GPP format SMS dispatcher. 1168 */ 1169 protected int dispatchGsmMessage(SmsMessageBase message) { 1170 return phone.mSMS.dispatchMessage(message); 1171 } 1172 1173 private void handleSms(byte[] ba) { 1174 if (ba[0] != 0) 1175 Log.d("ENF", "status : " + ba[0]); 1176 1177 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1178 // 3 == "received by MS from network; message to be read" 1179 if (ba[0] == 3) { 1180 int n = ba.length; 1181 1182 // Note: Data may include trailing FF's. That's OK; message 1183 // should still parse correctly. 1184 byte[] pdu = new byte[n - 1]; 1185 System.arraycopy(ba, 1, pdu, 0, n - 1); 1186 SmsMessage message = SmsMessage.createFromPdu(pdu); 1187 1188 dispatchGsmMessage(message); 1189 } 1190 } 1191 1192 1193 private void handleSmses(ArrayList messages) { 1194 int count = messages.size(); 1195 1196 for (int i = 0; i < count; i++) { 1197 byte[] ba = (byte[]) messages.get(i); 1198 1199 if (ba[0] != 0) 1200 Log.i("ENF", "status " + i + ": " + ba[0]); 1201 1202 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1203 // 3 == "received by MS from network; message to be read" 1204 1205 if (ba[0] == 3) { 1206 int n = ba.length; 1207 1208 // Note: Data may include trailing FF's. That's OK; message 1209 // should still parse correctly. 1210 byte[] pdu = new byte[n - 1]; 1211 System.arraycopy(ba, 1, pdu, 0, n - 1); 1212 SmsMessage message = SmsMessage.createFromPdu(pdu); 1213 1214 dispatchGsmMessage(message); 1215 1216 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1217 // 1 == "received by MS from network; message read" 1218 1219 ba[0] = 1; 1220 1221 if (false) { // XXX writing seems to crash RdoServD 1222 phone.getIccFileHandler().updateEFLinearFixed(EF_SMS, 1223 i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i)); 1224 } 1225 } 1226 } 1227 } 1228 1229 protected void onRecordLoaded() { 1230 // One record loaded successfully or failed, In either case 1231 // we need to update the recordsToLoad count 1232 recordsToLoad -= 1; 1233 1234 if (recordsToLoad == 0 && recordsRequested == true) { 1235 onAllRecordsLoaded(); 1236 } else if (recordsToLoad < 0) { 1237 Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected"); 1238 recordsToLoad = 0; 1239 } 1240 } 1241 1242 protected void onAllRecordsLoaded() { 1243 Log.d(LOG_TAG, "SIMRecords: record load complete"); 1244 1245 String operator = getOperatorNumeric(); 1246 1247 // Some fields require more than one SIM record to set 1248 1249 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator); 1250 1251 if (imsi != null) { 1252 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, 1253 MccTable.countryCodeForMcc(Integer.parseInt(imsi.substring(0,3)))); 1254 } 1255 else { 1256 Log.e("SIM", "[SIMRecords] onAllRecordsLoaded: imsi is NULL!"); 1257 } 1258 1259 setVoiceMailByCountry(operator); 1260 setSpnFromConfig(operator); 1261 1262 recordsLoadedRegistrants.notifyRegistrants( 1263 new AsyncResult(null, null, null)); 1264 phone.mIccCard.broadcastIccStateChangedIntent( 1265 SimCard.INTENT_VALUE_ICC_LOADED, null); 1266 } 1267 1268 //***** Private methods 1269 1270 private void setSpnFromConfig(String carrier) { 1271 if (mSpnOverride.containsCarrier(carrier)) { 1272 spn = mSpnOverride.getSpn(carrier); 1273 } 1274 } 1275 1276 1277 private void setVoiceMailByCountry (String spn) { 1278 if (mVmConfig.containsCarrier(spn)) { 1279 isVoiceMailFixed = true; 1280 voiceMailNum = mVmConfig.getVoiceMailNumber(spn); 1281 voiceMailTag = mVmConfig.getVoiceMailTag(spn); 1282 } 1283 } 1284 1285 public void onSimReady() { 1286 /* broadcast intent SIM_READY here so that we can make sure 1287 READY is sent before IMSI ready 1288 */ 1289 phone.mIccCard.broadcastIccStateChangedIntent( 1290 SimCard.INTENT_VALUE_ICC_READY, null); 1291 1292 fetchSimRecords(); 1293 } 1294 1295 protected void fetchSimRecords() { 1296 recordsRequested = true; 1297 IccFileHandler iccFh = phone.getIccFileHandler(); 1298 1299 Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad); 1300 1301 phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE)); 1302 recordsToLoad++; 1303 1304 iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); 1305 recordsToLoad++; 1306 1307 // FIXME should examine EF[MSISDN]'s capability configuration 1308 // to determine which is the voice/data/fax line 1309 new AdnRecordLoader(phone).loadFromEF(EF_MSISDN, EF_EXT1, 1, 1310 obtainMessage(EVENT_GET_MSISDN_DONE)); 1311 recordsToLoad++; 1312 1313 // Record number is subscriber profile 1314 iccFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE)); 1315 recordsToLoad++; 1316 1317 iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE)); 1318 recordsToLoad++; 1319 1320 // Record number is subscriber profile 1321 iccFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE)); 1322 recordsToLoad++; 1323 1324 1325 // Also load CPHS-style voice mail indicator, which stores 1326 // the same info as EF[MWIS]. If both exist, both are updated 1327 // but the EF[MWIS] data is preferred 1328 // Please note this must be loaded after EF[MWIS] 1329 iccFh.loadEFTransparent( 1330 EF_VOICE_MAIL_INDICATOR_CPHS, 1331 obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE)); 1332 recordsToLoad++; 1333 1334 // Same goes for Call Forward Status indicator: fetch both 1335 // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred. 1336 iccFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE)); 1337 recordsToLoad++; 1338 iccFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE)); 1339 recordsToLoad++; 1340 1341 1342 getSpnFsm(true, null); 1343 1344 iccFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE)); 1345 recordsToLoad++; 1346 1347 iccFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE)); 1348 recordsToLoad++; 1349 1350 iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE)); 1351 recordsToLoad++; 1352 1353 iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE)); 1354 recordsToLoad++; 1355 1356 iccFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE)); 1357 recordsToLoad++; 1358 1359 // XXX should seek instead of examining them all 1360 if (false) { // XXX 1361 iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE)); 1362 recordsToLoad++; 1363 } 1364 1365 if (CRASH_RIL) { 1366 String sms = "0107912160130310f20404d0110041007030208054832b0120" 1367 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1368 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1369 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1370 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1371 + "ffffffffffffffffffffffffffffff"; 1372 byte[] ba = IccUtils.hexStringToBytes(sms); 1373 1374 iccFh.updateEFLinearFixed(EF_SMS, 1, ba, null, 1375 obtainMessage(EVENT_MARK_SMS_READ_DONE, 1)); 1376 } 1377 } 1378 1379 /** 1380 * Returns the SpnDisplayRule based on settings on the SIM and the 1381 * specified plmn (currently-registered PLMN). See TS 22.101 Annex A 1382 * and TS 51.011 10.3.11 for details. 1383 * 1384 * If the SPN is not found on the SIM, the rule is always PLMN_ONLY. 1385 */ 1386 @Override 1387 public int getDisplayRule(String plmn) { 1388 int rule; 1389 if (spn == null || spnDisplayCondition == -1) { 1390 // EF_SPN was not found on the SIM, or not yet loaded. Just show ONS. 1391 rule = SPN_RULE_SHOW_PLMN; 1392 } else if (isOnMatchingPlmn(plmn)) { 1393 rule = SPN_RULE_SHOW_SPN; 1394 if ((spnDisplayCondition & 0x01) == 0x01) { 1395 // ONS required when registered to HPLMN or PLMN in EF_SPDI 1396 rule |= SPN_RULE_SHOW_PLMN; 1397 } 1398 } else { 1399 rule = SPN_RULE_SHOW_PLMN; 1400 if ((spnDisplayCondition & 0x02) == 0x00) { 1401 // SPN required if not registered to HPLMN or PLMN in EF_SPDI 1402 rule |= SPN_RULE_SHOW_SPN; 1403 } 1404 } 1405 return rule; 1406 } 1407 1408 /** 1409 * Checks if plmn is HPLMN or on the spdiNetworks list. 1410 */ 1411 private boolean isOnMatchingPlmn(String plmn) { 1412 if (plmn == null) return false; 1413 1414 if (plmn.equals(getOperatorNumeric())) { 1415 return true; 1416 } 1417 1418 if (spdiNetworks != null) { 1419 for (String spdiNet : spdiNetworks) { 1420 if (plmn.equals(spdiNet)) { 1421 return true; 1422 } 1423 } 1424 } 1425 return false; 1426 } 1427 1428 /** 1429 * States of Get SPN Finite State Machine which only used by getSpnFsm() 1430 */ 1431 private enum Get_Spn_Fsm_State { 1432 IDLE, // No initialized 1433 INIT, // Start FSM 1434 READ_SPN_3GPP, // Load EF_SPN firstly 1435 READ_SPN_CPHS, // Load EF_SPN_CPHS secondly 1436 READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last 1437 } 1438 1439 /** 1440 * Finite State Machine to load Service Provider Name , which can be stored 1441 * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2) 1442 * 1443 * After starting, FSM will search SPN EFs in order and stop after finding 1444 * the first valid SPN 1445 * 1446 * @param start set true only for initialize loading 1447 * @param ar the AsyncResult from loadEFTransparent 1448 * ar.exception holds exception in error 1449 * ar.result is byte[] for data in success 1450 */ 1451 private void getSpnFsm(boolean start, AsyncResult ar) { 1452 byte[] data; 1453 1454 if (start) { 1455 spnState = Get_Spn_Fsm_State.INIT; 1456 } 1457 1458 switch(spnState){ 1459 case INIT: 1460 spn = null; 1461 1462 phone.getIccFileHandler().loadEFTransparent( EF_SPN, 1463 obtainMessage(EVENT_GET_SPN_DONE)); 1464 recordsToLoad++; 1465 1466 spnState = Get_Spn_Fsm_State.READ_SPN_3GPP; 1467 break; 1468 case READ_SPN_3GPP: 1469 if (ar != null && ar.exception == null) { 1470 data = (byte[]) ar.result; 1471 spnDisplayCondition = 0xff & data[0]; 1472 spn = IccUtils.adnStringFieldToString(data, 1, data.length - 1); 1473 1474 if (DBG) log("Load EF_SPN: " + spn 1475 + " spnDisplayCondition: " + spnDisplayCondition); 1476 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); 1477 1478 spnState = Get_Spn_Fsm_State.IDLE; 1479 } else { 1480 phone.getIccFileHandler().loadEFTransparent( EF_SPN_CPHS, 1481 obtainMessage(EVENT_GET_SPN_DONE)); 1482 recordsToLoad++; 1483 1484 spnState = Get_Spn_Fsm_State.READ_SPN_CPHS; 1485 1486 // See TS 51.011 10.3.11. Basically, default to 1487 // show PLMN always, and SPN also if roaming. 1488 spnDisplayCondition = -1; 1489 } 1490 break; 1491 case READ_SPN_CPHS: 1492 if (ar != null && ar.exception == null) { 1493 data = (byte[]) ar.result; 1494 spn = IccUtils.adnStringFieldToString( 1495 data, 0, data.length - 1 ); 1496 1497 if (DBG) log("Load EF_SPN_CPHS: " + spn); 1498 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); 1499 1500 spnState = Get_Spn_Fsm_State.IDLE; 1501 } else { 1502 phone.getIccFileHandler().loadEFTransparent( 1503 EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE)); 1504 recordsToLoad++; 1505 1506 spnState = Get_Spn_Fsm_State.READ_SPN_SHORT_CPHS; 1507 } 1508 break; 1509 case READ_SPN_SHORT_CPHS: 1510 if (ar != null && ar.exception == null) { 1511 data = (byte[]) ar.result; 1512 spn = IccUtils.adnStringFieldToString( 1513 data, 0, data.length - 1); 1514 1515 if (DBG) log("Load EF_SPN_SHORT_CPHS: " + spn); 1516 phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); 1517 }else { 1518 if (DBG) log("No SPN loaded in either CHPS or 3GPP"); 1519 } 1520 1521 spnState = Get_Spn_Fsm_State.IDLE; 1522 break; 1523 default: 1524 spnState = Get_Spn_Fsm_State.IDLE; 1525 } 1526 } 1527 1528 /** 1529 * Parse TS 51.011 EF[SPDI] record 1530 * This record contains the list of numeric network IDs that 1531 * are treated specially when determining SPN display 1532 */ 1533 private void 1534 parseEfSpdi(byte[] data) { 1535 SimTlv tlv = new SimTlv(data, 0, data.length); 1536 1537 byte[] plmnEntries = null; 1538 1539 for ( ; tlv.isValidObject() ; tlv.nextObject()) { 1540 // Skip SPDI tag, if existant 1541 if (tlv.getTag() == TAG_SPDI) { 1542 tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length); 1543 } 1544 // There should only be one TAG_SPDI_PLMN_LIST 1545 if (tlv.getTag() == TAG_SPDI_PLMN_LIST) { 1546 plmnEntries = tlv.getData(); 1547 break; 1548 } 1549 } 1550 1551 if (plmnEntries == null) { 1552 return; 1553 } 1554 1555 spdiNetworks = new ArrayList<String>(plmnEntries.length / 3); 1556 1557 for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) { 1558 String plmnCode; 1559 plmnCode = IccUtils.bcdToString(plmnEntries, i, 3); 1560 1561 // Valid operator codes are 5 or 6 digits 1562 if (plmnCode.length() >= 5) { 1563 log("EF_SPDI network: " + plmnCode); 1564 spdiNetworks.add(plmnCode); 1565 } 1566 } 1567 } 1568 1569 /** 1570 * check to see if Mailbox Number is allocated and activated in CPHS SST 1571 */ 1572 private boolean isCphsMailboxEnabled() { 1573 if (mCphsInfo == null) return false; 1574 return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED ); 1575 } 1576 1577 protected void log(String s) { 1578 Log.d(LOG_TAG, "[SIMRecords] " + s); 1579 } 1580 1581 protected void loge(String s) { 1582 Log.e(LOG_TAG, "[SIMRecords] " + s); 1583 } 1584 1585 /** 1586 * Return true if "Restriction of menu options for manual PLMN selection" 1587 * bit is set or EF_CSP data is unavailable, return false otherwise. 1588 */ 1589 public boolean isCspPlmnEnabled() { 1590 return mCspPlmnEnabled; 1591 } 1592 1593 /** 1594 * Parse EF_CSP data and check if 1595 * "Restriction of menu options for manual PLMN selection" is 1596 * Enabled/Disabled 1597 * 1598 * @param data EF_CSP hex data. 1599 */ 1600 private void handleEfCspData(byte[] data) { 1601 // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined 1602 // 18 bytes (i.e 9 service groups info) and additional data specific to 1603 // operator. The valueAddedServicesGroup is not part of standard 1604 // services. This is operator specific and can be programmed any where. 1605 // Normally this is programmed as 10th service after the standard 1606 // services. 1607 int usedCspGroups = data.length / 2; 1608 // This is the "Servive Group Number" of "Value Added Services Group". 1609 byte valueAddedServicesGroup = (byte)0xC0; 1610 1611 mCspPlmnEnabled = true; 1612 for (int i = 0; i < usedCspGroups; i++) { 1613 if (data[2 * i] == valueAddedServicesGroup) { 1614 Log.i(LOG_TAG, "[CSP] found ValueAddedServicesGroup, value " 1615 + data[(2 * i) + 1]); 1616 if ((data[(2 * i) + 1] & 0x80) == 0x80) { 1617 // Bit 8 is for 1618 // "Restriction of menu options for manual PLMN selection". 1619 // Operator Selection menu should be enabled. 1620 mCspPlmnEnabled = true; 1621 } else { 1622 mCspPlmnEnabled = false; 1623 // Operator Selection menu should be disabled. 1624 // Operator Selection Mode should be set to Automatic. 1625 Log.i(LOG_TAG,"[CSP] Set Automatic Network Selection"); 1626 phone.setNetworkSelectionModeAutomatic(null); 1627 } 1628 return; 1629 } 1630 } 1631 1632 Log.w(LOG_TAG, "[CSP] Value Added Service Group (0xC0), not found!"); 1633 } 1634 } 1635