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