SIMRecords.java revision 67aacea4e6b0199579a71b3a6a2f5d6778b8a002
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.Message; 25import android.os.SystemProperties; 26import android.telephony.PhoneNumberUtils; 27import android.telephony.SmsMessage; 28import android.text.TextUtils; 29import android.telephony.Rlog; 30 31import com.android.internal.telephony.CommandsInterface; 32import com.android.internal.telephony.MccTable; 33import com.android.internal.telephony.SmsConstants; 34import com.android.internal.telephony.gsm.SimTlv; 35 36import java.io.FileDescriptor; 37import java.io.PrintWriter; 38import java.util.ArrayList; 39import java.util.Arrays; 40 41/** 42 * {@hide} 43 */ 44public 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 case EF_FDN: 1196 if (DBG) log("SIM Refresh called for EF_FDN"); 1197 mParentApp.queryFdn(); 1198 break; 1199 default: 1200 // For now, fetch all records if this is not a 1201 // voicemail number. 1202 // TODO: Handle other cases, instead of fetching all. 1203 mAdnCache.reset(); 1204 fetchSimRecords(); 1205 break; 1206 } 1207 } 1208 1209 private void handleSimRefresh(IccRefreshResponse refreshResponse){ 1210 if (refreshResponse == null) { 1211 if (DBG) log("handleSimRefresh received without input"); 1212 return; 1213 } 1214 1215 if (refreshResponse.aid != null && 1216 !refreshResponse.aid.equals(mParentApp.getAid())) { 1217 // This is for different app. Ignore. 1218 return; 1219 } 1220 1221 switch (refreshResponse.refreshResult) { 1222 case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE: 1223 if (DBG) log("handleSimRefresh with SIM_FILE_UPDATED"); 1224 handleFileUpdate(refreshResponse.efId); 1225 break; 1226 case IccRefreshResponse.REFRESH_RESULT_INIT: 1227 if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT"); 1228 // need to reload all files (that we care about) 1229 onIccRefreshInit(); 1230 break; 1231 case IccRefreshResponse.REFRESH_RESULT_RESET: 1232 if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET"); 1233 mCi.setRadioPower(false, null); 1234 /* Note: no need to call setRadioPower(true). Assuming the desired 1235 * radio power state is still ON (as tracked by ServiceStateTracker), 1236 * ServiceStateTracker will call setRadioPower when it receives the 1237 * RADIO_STATE_CHANGED notification for the power off. And if the 1238 * desired power state has changed in the interim, we don't want to 1239 * override it with an unconditional power on. 1240 */ 1241 break; 1242 default: 1243 // unknown refresh operation 1244 if (DBG) log("handleSimRefresh with unknown operation"); 1245 break; 1246 } 1247 } 1248 1249 /** 1250 * Dispatch 3GPP format message to registrant ({@code GSMPhone} or {@code CDMALTEPhone}) 1251 * to pass to the 3GPP SMS dispatcher for delivery. 1252 */ 1253 private int dispatchGsmMessage(SmsMessage message) { 1254 mNewSmsRegistrants.notifyResult(message); 1255 return 0; 1256 } 1257 1258 private void handleSms(byte[] ba) { 1259 if (ba[0] != 0) 1260 Rlog.d("ENF", "status : " + ba[0]); 1261 1262 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1263 // 3 == "received by MS from network; message to be read" 1264 if (ba[0] == 3) { 1265 int n = ba.length; 1266 1267 // Note: Data may include trailing FF's. That's OK; message 1268 // should still parse correctly. 1269 byte[] pdu = new byte[n - 1]; 1270 System.arraycopy(ba, 1, pdu, 0, n - 1); 1271 SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP); 1272 1273 dispatchGsmMessage(message); 1274 } 1275 } 1276 1277 1278 private void handleSmses(ArrayList<byte[]> messages) { 1279 int count = messages.size(); 1280 1281 for (int i = 0; i < count; i++) { 1282 byte[] ba = messages.get(i); 1283 1284 if (ba[0] != 0) 1285 Rlog.i("ENF", "status " + i + ": " + ba[0]); 1286 1287 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1288 // 3 == "received by MS from network; message to be read" 1289 1290 if (ba[0] == 3) { 1291 int n = ba.length; 1292 1293 // Note: Data may include trailing FF's. That's OK; message 1294 // should still parse correctly. 1295 byte[] pdu = new byte[n - 1]; 1296 System.arraycopy(ba, 1, pdu, 0, n - 1); 1297 SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP); 1298 1299 dispatchGsmMessage(message); 1300 1301 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1302 // 1 == "received by MS from network; message read" 1303 1304 ba[0] = 1; 1305 1306 if (false) { // FIXME: writing seems to crash RdoServD 1307 mFh.updateEFLinearFixed(EF_SMS, 1308 i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i)); 1309 } 1310 } 1311 } 1312 } 1313 1314 @Override 1315 protected void onRecordLoaded() { 1316 // One record loaded successfully or failed, In either case 1317 // we need to update the recordsToLoad count 1318 mRecordsToLoad -= 1; 1319 if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested); 1320 1321 if (mRecordsToLoad == 0 && mRecordsRequested == true) { 1322 onAllRecordsLoaded(); 1323 } else if (mRecordsToLoad < 0) { 1324 loge("recordsToLoad <0, programmer error suspected"); 1325 mRecordsToLoad = 0; 1326 } 1327 } 1328 1329 @Override 1330 protected void onAllRecordsLoaded() { 1331 if (DBG) log("record load complete"); 1332 1333 // Some fields require more than one SIM record to set 1334 1335 String operator = getOperatorNumeric(); 1336 if (!TextUtils.isEmpty(operator)) { 1337 log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" + 1338 operator + "'"); 1339 SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator); 1340 } else { 1341 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping"); 1342 } 1343 1344 if (!TextUtils.isEmpty(mImsi)) { 1345 log("onAllRecordsLoaded set mcc imsi=" + mImsi); 1346 SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, 1347 MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3)))); 1348 } else { 1349 log("onAllRecordsLoaded empty imsi skipping setting mcc"); 1350 } 1351 1352 setVoiceMailByCountry(operator); 1353 setSpnFromConfig(operator); 1354 1355 mRecordsLoadedRegistrants.notifyRegistrants( 1356 new AsyncResult(null, null, null)); 1357 } 1358 1359 //***** Private methods 1360 1361 private void setSpnFromConfig(String carrier) { 1362 if (mSpnOverride.containsCarrier(carrier)) { 1363 mSpn = mSpnOverride.getSpn(carrier); 1364 } 1365 } 1366 1367 1368 private void setVoiceMailByCountry (String spn) { 1369 if (mVmConfig.containsCarrier(spn)) { 1370 mIsVoiceMailFixed = true; 1371 mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn); 1372 mVoiceMailTag = mVmConfig.getVoiceMailTag(spn); 1373 } 1374 } 1375 1376 @Override 1377 public void onReady() { 1378 fetchSimRecords(); 1379 } 1380 1381 protected void fetchSimRecords() { 1382 mRecordsRequested = true; 1383 1384 if (DBG) log("fetchSimRecords " + mRecordsToLoad); 1385 1386 mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); 1387 mRecordsToLoad++; 1388 1389 mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); 1390 mRecordsToLoad++; 1391 1392 // FIXME should examine EF[MSISDN]'s capability configuration 1393 // to determine which is the voice/data/fax line 1394 new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1, 1395 obtainMessage(EVENT_GET_MSISDN_DONE)); 1396 mRecordsToLoad++; 1397 1398 // Record number is subscriber profile 1399 mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE)); 1400 mRecordsToLoad++; 1401 1402 mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE)); 1403 mRecordsToLoad++; 1404 1405 // Record number is subscriber profile 1406 mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE)); 1407 mRecordsToLoad++; 1408 1409 1410 // Also load CPHS-style voice mail indicator, which stores 1411 // the same info as EF[MWIS]. If both exist, both are updated 1412 // but the EF[MWIS] data is preferred 1413 // Please note this must be loaded after EF[MWIS] 1414 mFh.loadEFTransparent( 1415 EF_VOICE_MAIL_INDICATOR_CPHS, 1416 obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE)); 1417 mRecordsToLoad++; 1418 1419 // Same goes for Call Forward Status indicator: fetch both 1420 // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred. 1421 mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE)); 1422 mRecordsToLoad++; 1423 mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE)); 1424 mRecordsToLoad++; 1425 1426 1427 getSpnFsm(true, null); 1428 1429 mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE)); 1430 mRecordsToLoad++; 1431 1432 mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE)); 1433 mRecordsToLoad++; 1434 1435 mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE)); 1436 mRecordsToLoad++; 1437 1438 mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE)); 1439 mRecordsToLoad++; 1440 1441 mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE)); 1442 mRecordsToLoad++; 1443 1444 mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE)); 1445 mRecordsToLoad++; 1446 1447 // XXX should seek instead of examining them all 1448 if (false) { // XXX 1449 mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE)); 1450 mRecordsToLoad++; 1451 } 1452 1453 if (CRASH_RIL) { 1454 String sms = "0107912160130310f20404d0110041007030208054832b0120" 1455 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1456 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1457 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1458 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1459 + "ffffffffffffffffffffffffffffff"; 1460 byte[] ba = IccUtils.hexStringToBytes(sms); 1461 1462 mFh.updateEFLinearFixed(EF_SMS, 1, ba, null, 1463 obtainMessage(EVENT_MARK_SMS_READ_DONE, 1)); 1464 } 1465 if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested); 1466 } 1467 1468 /** 1469 * Returns the SpnDisplayRule based on settings on the SIM and the 1470 * specified plmn (currently-registered PLMN). See TS 22.101 Annex A 1471 * and TS 51.011 10.3.11 for details. 1472 * 1473 * If the SPN is not found on the SIM or is empty, the rule is 1474 * always PLMN_ONLY. 1475 */ 1476 @Override 1477 public int getDisplayRule(String plmn) { 1478 int rule; 1479 if (TextUtils.isEmpty(mSpn) || mSpnDisplayCondition == -1) { 1480 // No EF_SPN content was found on the SIM, or not yet loaded. Just show ONS. 1481 rule = SPN_RULE_SHOW_PLMN; 1482 } else if (isOnMatchingPlmn(plmn)) { 1483 rule = SPN_RULE_SHOW_SPN; 1484 if ((mSpnDisplayCondition & 0x01) == 0x01) { 1485 // ONS required when registered to HPLMN or PLMN in EF_SPDI 1486 rule |= SPN_RULE_SHOW_PLMN; 1487 } 1488 } else { 1489 rule = SPN_RULE_SHOW_PLMN; 1490 if ((mSpnDisplayCondition & 0x02) == 0x00) { 1491 // SPN required if not registered to HPLMN or PLMN in EF_SPDI 1492 rule |= SPN_RULE_SHOW_SPN; 1493 } 1494 } 1495 return rule; 1496 } 1497 1498 /** 1499 * Checks if plmn is HPLMN or on the spdiNetworks list. 1500 */ 1501 private boolean isOnMatchingPlmn(String plmn) { 1502 if (plmn == null) return false; 1503 1504 if (plmn.equals(getOperatorNumeric())) { 1505 return true; 1506 } 1507 1508 if (mSpdiNetworks != null) { 1509 for (String spdiNet : mSpdiNetworks) { 1510 if (plmn.equals(spdiNet)) { 1511 return true; 1512 } 1513 } 1514 } 1515 return false; 1516 } 1517 1518 /** 1519 * States of Get SPN Finite State Machine which only used by getSpnFsm() 1520 */ 1521 private enum GetSpnFsmState { 1522 IDLE, // No initialized 1523 INIT, // Start FSM 1524 READ_SPN_3GPP, // Load EF_SPN firstly 1525 READ_SPN_CPHS, // Load EF_SPN_CPHS secondly 1526 READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last 1527 } 1528 1529 /** 1530 * Finite State Machine to load Service Provider Name , which can be stored 1531 * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2) 1532 * 1533 * After starting, FSM will search SPN EFs in order and stop after finding 1534 * the first valid SPN 1535 * 1536 * If the FSM gets restart while waiting for one of 1537 * SPN EFs results (i.e. a SIM refresh occurs after issuing 1538 * read EF_CPHS_SPN), it will re-initialize only after 1539 * receiving and discarding the unfinished SPN EF result. 1540 * 1541 * @param start set true only for initialize loading 1542 * @param ar the AsyncResult from loadEFTransparent 1543 * ar.exception holds exception in error 1544 * ar.result is byte[] for data in success 1545 */ 1546 private void getSpnFsm(boolean start, AsyncResult ar) { 1547 byte[] data; 1548 1549 if (start) { 1550 // Check previous state to see if there is outstanding 1551 // SPN read 1552 if(mSpnState == GetSpnFsmState.READ_SPN_3GPP || 1553 mSpnState == GetSpnFsmState.READ_SPN_CPHS || 1554 mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS || 1555 mSpnState == GetSpnFsmState.INIT) { 1556 // Set INIT then return so the INIT code 1557 // will run when the outstanding read done. 1558 mSpnState = GetSpnFsmState.INIT; 1559 return; 1560 } else { 1561 mSpnState = GetSpnFsmState.INIT; 1562 } 1563 } 1564 1565 switch(mSpnState){ 1566 case INIT: 1567 mSpn = null; 1568 1569 mFh.loadEFTransparent(EF_SPN, 1570 obtainMessage(EVENT_GET_SPN_DONE)); 1571 mRecordsToLoad++; 1572 1573 mSpnState = GetSpnFsmState.READ_SPN_3GPP; 1574 break; 1575 case READ_SPN_3GPP: 1576 if (ar != null && ar.exception == null) { 1577 data = (byte[]) ar.result; 1578 mSpnDisplayCondition = 0xff & data[0]; 1579 mSpn = IccUtils.adnStringFieldToString(data, 1, data.length - 1); 1580 1581 if (DBG) log("Load EF_SPN: " + mSpn 1582 + " spnDisplayCondition: " + mSpnDisplayCondition); 1583 SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn); 1584 1585 mSpnState = GetSpnFsmState.IDLE; 1586 } else { 1587 mFh.loadEFTransparent( EF_SPN_CPHS, 1588 obtainMessage(EVENT_GET_SPN_DONE)); 1589 mRecordsToLoad++; 1590 1591 mSpnState = GetSpnFsmState.READ_SPN_CPHS; 1592 1593 // See TS 51.011 10.3.11. Basically, default to 1594 // show PLMN always, and SPN also if roaming. 1595 mSpnDisplayCondition = -1; 1596 } 1597 break; 1598 case READ_SPN_CPHS: 1599 if (ar != null && ar.exception == null) { 1600 data = (byte[]) ar.result; 1601 mSpn = IccUtils.adnStringFieldToString(data, 0, data.length); 1602 1603 if (DBG) log("Load EF_SPN_CPHS: " + mSpn); 1604 SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn); 1605 1606 mSpnState = GetSpnFsmState.IDLE; 1607 } else { 1608 mFh.loadEFTransparent( 1609 EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE)); 1610 mRecordsToLoad++; 1611 1612 mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS; 1613 } 1614 break; 1615 case READ_SPN_SHORT_CPHS: 1616 if (ar != null && ar.exception == null) { 1617 data = (byte[]) ar.result; 1618 mSpn = IccUtils.adnStringFieldToString(data, 0, data.length); 1619 1620 if (DBG) log("Load EF_SPN_SHORT_CPHS: " + mSpn); 1621 SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn); 1622 }else { 1623 if (DBG) log("No SPN loaded in either CHPS or 3GPP"); 1624 } 1625 1626 mSpnState = GetSpnFsmState.IDLE; 1627 break; 1628 default: 1629 mSpnState = GetSpnFsmState.IDLE; 1630 } 1631 } 1632 1633 /** 1634 * Parse TS 51.011 EF[SPDI] record 1635 * This record contains the list of numeric network IDs that 1636 * are treated specially when determining SPN display 1637 */ 1638 private void 1639 parseEfSpdi(byte[] data) { 1640 SimTlv tlv = new SimTlv(data, 0, data.length); 1641 1642 byte[] plmnEntries = null; 1643 1644 for ( ; tlv.isValidObject() ; tlv.nextObject()) { 1645 // Skip SPDI tag, if existant 1646 if (tlv.getTag() == TAG_SPDI) { 1647 tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length); 1648 } 1649 // There should only be one TAG_SPDI_PLMN_LIST 1650 if (tlv.getTag() == TAG_SPDI_PLMN_LIST) { 1651 plmnEntries = tlv.getData(); 1652 break; 1653 } 1654 } 1655 1656 if (plmnEntries == null) { 1657 return; 1658 } 1659 1660 mSpdiNetworks = new ArrayList<String>(plmnEntries.length / 3); 1661 1662 for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) { 1663 String plmnCode; 1664 plmnCode = IccUtils.bcdToString(plmnEntries, i, 3); 1665 1666 // Valid operator codes are 5 or 6 digits 1667 if (plmnCode.length() >= 5) { 1668 log("EF_SPDI network: " + plmnCode); 1669 mSpdiNetworks.add(plmnCode); 1670 } 1671 } 1672 } 1673 1674 /** 1675 * check to see if Mailbox Number is allocated and activated in CPHS SST 1676 */ 1677 private boolean isCphsMailboxEnabled() { 1678 if (mCphsInfo == null) return false; 1679 return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED ); 1680 } 1681 1682 @Override 1683 protected void log(String s) { 1684 Rlog.d(LOG_TAG, "[SIMRecords] " + s); 1685 } 1686 1687 @Override 1688 protected void loge(String s) { 1689 Rlog.e(LOG_TAG, "[SIMRecords] " + s); 1690 } 1691 1692 protected void logw(String s, Throwable tr) { 1693 Rlog.w(LOG_TAG, "[SIMRecords] " + s, tr); 1694 } 1695 1696 protected void logv(String s) { 1697 Rlog.v(LOG_TAG, "[SIMRecords] " + s); 1698 } 1699 1700 /** 1701 * Return true if "Restriction of menu options for manual PLMN selection" 1702 * bit is set or EF_CSP data is unavailable, return false otherwise. 1703 */ 1704 @Override 1705 public boolean isCspPlmnEnabled() { 1706 return mCspPlmnEnabled; 1707 } 1708 1709 /** 1710 * Parse EF_CSP data and check if 1711 * "Restriction of menu options for manual PLMN selection" is 1712 * Enabled/Disabled 1713 * 1714 * @param data EF_CSP hex data. 1715 */ 1716 private void handleEfCspData(byte[] data) { 1717 // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined 1718 // 18 bytes (i.e 9 service groups info) and additional data specific to 1719 // operator. The valueAddedServicesGroup is not part of standard 1720 // services. This is operator specific and can be programmed any where. 1721 // Normally this is programmed as 10th service after the standard 1722 // services. 1723 int usedCspGroups = data.length / 2; 1724 // This is the "Service Group Number" of "Value Added Services Group". 1725 byte valueAddedServicesGroup = (byte)0xC0; 1726 1727 mCspPlmnEnabled = true; 1728 for (int i = 0; i < usedCspGroups; i++) { 1729 if (data[2 * i] == valueAddedServicesGroup) { 1730 log("[CSP] found ValueAddedServicesGroup, value " + data[(2 * i) + 1]); 1731 if ((data[(2 * i) + 1] & 0x80) == 0x80) { 1732 // Bit 8 is for 1733 // "Restriction of menu options for manual PLMN selection". 1734 // Operator Selection menu should be enabled. 1735 mCspPlmnEnabled = true; 1736 } else { 1737 mCspPlmnEnabled = false; 1738 // Operator Selection menu should be disabled. 1739 // Operator Selection Mode should be set to Automatic. 1740 log("[CSP] Set Automatic Network Selection"); 1741 mNetworkSelectionModeAutomaticRegistrants.notifyRegistrants(); 1742 } 1743 return; 1744 } 1745 } 1746 1747 log("[CSP] Value Added Service Group (0xC0), not found!"); 1748 } 1749 1750 @Override 1751 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1752 pw.println("SIMRecords: " + this); 1753 pw.println(" extends:"); 1754 super.dump(fd, pw, args); 1755 pw.println(" mVmConfig=" + mVmConfig); 1756 pw.println(" mSpnOverride=" + mSpnOverride); 1757 pw.println(" mCallForwardingEnabled=" + mCallForwardingEnabled); 1758 pw.println(" mSpnState=" + mSpnState); 1759 pw.println(" mCphsInfo=" + mCphsInfo); 1760 pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled); 1761 pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS)); 1762 pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI)); 1763 pw.println(" mEfCff[]=" + Arrays.toString(mEfCff)); 1764 pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis)); 1765 pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition); 1766 pw.println(" mSpdiNetworks[]=" + mSpdiNetworks); 1767 pw.println(" mPnnHomeName=" + mPnnHomeName); 1768 pw.println(" mUsimServiceTable=" + mUsimServiceTable); 1769 pw.println(" mGid1=" + mGid1); 1770 pw.flush(); 1771 } 1772} 1773