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