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, 629 mImsi.substring(0, 3 + mMncLength), false); 630 } 631 mImsiReadyRegistrants.notifyRegistrants(); 632 break; 633 634 case EVENT_GET_MBI_DONE: 635 boolean isValidMbdn; 636 isRecordLoadResponse = true; 637 638 ar = (AsyncResult)msg.obj; 639 data = (byte[]) ar.result; 640 641 isValidMbdn = false; 642 if (ar.exception == null) { 643 // Refer TS 51.011 Section 10.3.44 for content details 644 log("EF_MBI: " + IccUtils.bytesToHexString(data)); 645 646 // Voice mail record number stored first 647 mMailboxIndex = data[0] & 0xff; 648 649 // check if dailing numbe id valid 650 if (mMailboxIndex != 0 && mMailboxIndex != 0xff) { 651 log("Got valid mailbox number for MBDN"); 652 isValidMbdn = true; 653 } 654 } 655 656 // one more record to load 657 mRecordsToLoad += 1; 658 659 if (isValidMbdn) { 660 // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED 661 new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6, 662 mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE)); 663 } else { 664 // If this EF not present, try mailbox as in CPHS standard 665 // CPHS (CPHS4_2.WW6) is a european standard. 666 new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS, 667 EF_EXT1, 1, 668 obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 669 } 670 671 break; 672 case EVENT_GET_CPHS_MAILBOX_DONE: 673 case EVENT_GET_MBDN_DONE: 674 //Resetting the voice mail number and voice mail tag to null 675 //as these should be updated from the data read from EF_MBDN. 676 //If they are not reset, incase of invalid data/exception these 677 //variables are retaining their previous values and are 678 //causing invalid voice mailbox info display to user. 679 mVoiceMailNum = null; 680 mVoiceMailTag = null; 681 isRecordLoadResponse = true; 682 683 ar = (AsyncResult)msg.obj; 684 685 if (ar.exception != null) { 686 687 log("Invalid or missing EF" 688 + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]")); 689 690 // Bug #645770 fall back to CPHS 691 // FIXME should use SST to decide 692 693 if (msg.what == EVENT_GET_MBDN_DONE) { 694 //load CPHS on fail... 695 // FIXME right now, only load line1's CPHS voice mail entry 696 697 mRecordsToLoad += 1; 698 new AdnRecordLoader(mFh).loadFromEF( 699 EF_MAILBOX_CPHS, EF_EXT1, 1, 700 obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 701 } 702 break; 703 } 704 705 adn = (AdnRecord)ar.result; 706 707 log("VM: " + adn + 708 ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]")); 709 710 if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) { 711 // Bug #645770 fall back to CPHS 712 // FIXME should use SST to decide 713 // FIXME right now, only load line1's CPHS voice mail entry 714 mRecordsToLoad += 1; 715 new AdnRecordLoader(mFh).loadFromEF( 716 EF_MAILBOX_CPHS, EF_EXT1, 1, 717 obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 718 719 break; 720 } 721 722 mVoiceMailNum = adn.getNumber(); 723 mVoiceMailTag = adn.getAlphaTag(); 724 break; 725 726 case EVENT_GET_MSISDN_DONE: 727 isRecordLoadResponse = true; 728 729 ar = (AsyncResult)msg.obj; 730 731 if (ar.exception != null) { 732 log("Invalid or missing EF[MSISDN]"); 733 break; 734 } 735 736 adn = (AdnRecord)ar.result; 737 738 mMsisdn = adn.getNumber(); 739 mMsisdnTag = adn.getAlphaTag(); 740 741 log("MSISDN: " + /*mMsisdn*/ "xxxxxxx"); 742 break; 743 744 case EVENT_SET_MSISDN_DONE: 745 isRecordLoadResponse = false; 746 ar = (AsyncResult)msg.obj; 747 748 if (ar.userObj != null) { 749 AsyncResult.forMessage(((Message) ar.userObj)).exception 750 = ar.exception; 751 ((Message) ar.userObj).sendToTarget(); 752 } 753 break; 754 755 case EVENT_GET_MWIS_DONE: 756 isRecordLoadResponse = true; 757 758 ar = (AsyncResult)msg.obj; 759 data = (byte[])ar.result; 760 761 if (ar.exception != null) { 762 break; 763 } 764 765 log("EF_MWIS: " + IccUtils.bytesToHexString(data)); 766 767 mEfMWIS = data; 768 769 if ((data[0] & 0xff) == 0xff) { 770 log("Uninitialized record MWIS"); 771 break; 772 } 773 774 // Refer TS 51.011 Section 10.3.45 for the content description 775 boolean voiceMailWaiting = ((data[0] & 0x01) != 0); 776 mCountVoiceMessages = data[1] & 0xff; 777 778 if (voiceMailWaiting && mCountVoiceMessages == 0) { 779 // Unknown count = -1 780 mCountVoiceMessages = -1; 781 } 782 783 mRecordsEventsRegistrants.notifyResult(EVENT_MWI); 784 break; 785 786 case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE: 787 isRecordLoadResponse = true; 788 789 ar = (AsyncResult)msg.obj; 790 data = (byte[])ar.result; 791 792 if (ar.exception != null) { 793 break; 794 } 795 796 mEfCPHS_MWI = data; 797 798 // Use this data if the EF[MWIS] exists and 799 // has been loaded 800 801 if (mEfMWIS == null) { 802 int indicator = data[0] & 0xf; 803 804 // Refer CPHS4_2.WW6 B4.2.3 805 if (indicator == 0xA) { 806 // Unknown count = -1 807 mCountVoiceMessages = -1; 808 } else if (indicator == 0x5) { 809 mCountVoiceMessages = 0; 810 } 811 812 mRecordsEventsRegistrants.notifyResult(EVENT_MWI); 813 } 814 break; 815 816 case EVENT_GET_ICCID_DONE: 817 isRecordLoadResponse = true; 818 819 ar = (AsyncResult)msg.obj; 820 data = (byte[])ar.result; 821 822 if (ar.exception != null) { 823 break; 824 } 825 826 mIccId = IccUtils.bcdToString(data, 0, data.length); 827 828 log("iccid: " + mIccId); 829 830 break; 831 832 833 case EVENT_GET_AD_DONE: 834 try { 835 isRecordLoadResponse = true; 836 837 ar = (AsyncResult)msg.obj; 838 data = (byte[])ar.result; 839 840 if (ar.exception != null) { 841 break; 842 } 843 844 log("EF_AD: " + IccUtils.bytesToHexString(data)); 845 846 if (data.length < 3) { 847 log("Corrupt AD data on SIM"); 848 break; 849 } 850 851 if (data.length == 3) { 852 log("MNC length not present in EF_AD"); 853 break; 854 } 855 856 mMncLength = data[3] & 0xf; 857 858 if (mMncLength == 0xf) { 859 mMncLength = UNKNOWN; 860 } 861 } finally { 862 if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN) || 863 (mMncLength == 2)) && ((mImsi != null) && (mImsi.length() >= 6))) { 864 String mccmncCode = mImsi.substring(0, 6); 865 for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) { 866 if (mccmnc.equals(mccmncCode)) { 867 mMncLength = 3; 868 break; 869 } 870 } 871 } 872 873 if (mMncLength == UNKNOWN || mMncLength == UNINITIALIZED) { 874 if (mImsi != null) { 875 try { 876 int mcc = Integer.parseInt(mImsi.substring(0,3)); 877 878 mMncLength = MccTable.smallestDigitsMccForMnc(mcc); 879 } catch (NumberFormatException e) { 880 mMncLength = UNKNOWN; 881 loge("Corrupt IMSI!"); 882 } 883 } else { 884 // Indicate we got this info, but it didn't contain the length. 885 mMncLength = UNKNOWN; 886 887 log("MNC length not present in EF_AD"); 888 } 889 } 890 if (mImsi != null && mMncLength != UNKNOWN) { 891 // finally have both imsi and the length of the mnc and can parse 892 // the imsi properly 893 MccTable.updateMccMncConfiguration(mContext, 894 mImsi.substring(0, 3 + mMncLength), false); 895 } 896 } 897 break; 898 899 case EVENT_GET_SPN_DONE: 900 isRecordLoadResponse = true; 901 ar = (AsyncResult) msg.obj; 902 getSpnFsm(false, ar); 903 break; 904 905 case EVENT_GET_CFF_DONE: 906 isRecordLoadResponse = true; 907 908 ar = (AsyncResult) msg.obj; 909 data = (byte[]) ar.result; 910 911 if (ar.exception != null) { 912 break; 913 } 914 915 log("EF_CFF_CPHS: " + IccUtils.bytesToHexString(data)); 916 mEfCff = data; 917 918 // if EF_CFIS is valid, prefer it to EF_CFF_CPHS 919 if (!validEfCfis(mEfCfis)) { 920 mCallForwardingEnabled = 921 ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE); 922 923 mRecordsEventsRegistrants.notifyResult(EVENT_CFI); 924 } else { 925 log("EVENT_GET_CFF_DONE: EF_CFIS is valid, ignoring EF_CFF_CPHS"); 926 } 927 break; 928 929 case EVENT_GET_SPDI_DONE: 930 isRecordLoadResponse = true; 931 932 ar = (AsyncResult)msg.obj; 933 data = (byte[])ar.result; 934 935 if (ar.exception != null) { 936 break; 937 } 938 939 parseEfSpdi(data); 940 break; 941 942 case EVENT_UPDATE_DONE: 943 ar = (AsyncResult)msg.obj; 944 if (ar.exception != null) { 945 logw("update failed. ", ar.exception); 946 } 947 break; 948 949 case EVENT_GET_PNN_DONE: 950 isRecordLoadResponse = true; 951 952 ar = (AsyncResult)msg.obj; 953 data = (byte[])ar.result; 954 955 if (ar.exception != null) { 956 break; 957 } 958 959 SimTlv tlv = new SimTlv(data, 0, data.length); 960 961 for ( ; tlv.isValidObject() ; tlv.nextObject()) { 962 if (tlv.getTag() == TAG_FULL_NETWORK_NAME) { 963 mPnnHomeName 964 = IccUtils.networkNameToString( 965 tlv.getData(), 0, tlv.getData().length); 966 break; 967 } 968 } 969 break; 970 971 case EVENT_GET_ALL_SMS_DONE: 972 isRecordLoadResponse = true; 973 974 ar = (AsyncResult)msg.obj; 975 if (ar.exception != null) 976 break; 977 978 handleSmses((ArrayList<byte []>) ar.result); 979 break; 980 981 case EVENT_MARK_SMS_READ_DONE: 982 Rlog.i("ENF", "marked read: sms " + msg.arg1); 983 break; 984 985 986 case EVENT_SMS_ON_SIM: 987 isRecordLoadResponse = false; 988 989 ar = (AsyncResult)msg.obj; 990 991 int[] index = (int[])ar.result; 992 993 if (ar.exception != null || index.length != 1) { 994 loge("Error on SMS_ON_SIM with exp " 995 + ar.exception + " length " + index.length); 996 } else { 997 log("READ EF_SMS RECORD index=" + index[0]); 998 mFh.loadEFLinearFixed(EF_SMS,index[0], 999 obtainMessage(EVENT_GET_SMS_DONE)); 1000 } 1001 break; 1002 1003 case EVENT_GET_SMS_DONE: 1004 isRecordLoadResponse = false; 1005 ar = (AsyncResult)msg.obj; 1006 if (ar.exception == null) { 1007 handleSms((byte[])ar.result); 1008 } else { 1009 loge("Error on GET_SMS with exp " + ar.exception); 1010 } 1011 break; 1012 case EVENT_GET_SST_DONE: 1013 isRecordLoadResponse = true; 1014 1015 ar = (AsyncResult)msg.obj; 1016 data = (byte[])ar.result; 1017 1018 if (ar.exception != null) { 1019 break; 1020 } 1021 1022 mUsimServiceTable = new UsimServiceTable(data); 1023 if (DBG) log("SST: " + mUsimServiceTable); 1024 break; 1025 1026 case EVENT_GET_INFO_CPHS_DONE: 1027 isRecordLoadResponse = true; 1028 1029 ar = (AsyncResult)msg.obj; 1030 1031 if (ar.exception != null) { 1032 break; 1033 } 1034 1035 mCphsInfo = (byte[])ar.result; 1036 1037 if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo)); 1038 break; 1039 1040 case EVENT_SET_MBDN_DONE: 1041 isRecordLoadResponse = false; 1042 ar = (AsyncResult)msg.obj; 1043 1044 if (ar.exception == null) { 1045 mVoiceMailNum = mNewVoiceMailNum; 1046 mVoiceMailTag = mNewVoiceMailTag; 1047 } 1048 1049 if (isCphsMailboxEnabled()) { 1050 adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum); 1051 Message onCphsCompleted = (Message) ar.userObj; 1052 1053 /* write to cphs mailbox whenever it is available but 1054 * we only need notify caller once if both updating are 1055 * successful. 1056 * 1057 * so if set_mbdn successful, notify caller here and set 1058 * onCphsCompleted to null 1059 */ 1060 if (ar.exception == null && ar.userObj != null) { 1061 AsyncResult.forMessage(((Message) ar.userObj)).exception 1062 = null; 1063 ((Message) ar.userObj).sendToTarget(); 1064 1065 if (DBG) log("Callback with MBDN successful."); 1066 1067 onCphsCompleted = null; 1068 } 1069 1070 new AdnRecordLoader(mFh). 1071 updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null, 1072 obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, 1073 onCphsCompleted)); 1074 } else { 1075 if (ar.userObj != null) { 1076 AsyncResult.forMessage(((Message) ar.userObj)).exception 1077 = ar.exception; 1078 ((Message) ar.userObj).sendToTarget(); 1079 } 1080 } 1081 break; 1082 case EVENT_SET_CPHS_MAILBOX_DONE: 1083 isRecordLoadResponse = false; 1084 ar = (AsyncResult)msg.obj; 1085 if(ar.exception == null) { 1086 mVoiceMailNum = mNewVoiceMailNum; 1087 mVoiceMailTag = mNewVoiceMailTag; 1088 } else { 1089 if (DBG) log("Set CPHS MailBox with exception: " 1090 + ar.exception); 1091 } 1092 if (ar.userObj != null) { 1093 if (DBG) log("Callback with CPHS MB successful."); 1094 AsyncResult.forMessage(((Message) ar.userObj)).exception 1095 = ar.exception; 1096 ((Message) ar.userObj).sendToTarget(); 1097 } 1098 break; 1099 case EVENT_SIM_REFRESH: 1100 isRecordLoadResponse = false; 1101 ar = (AsyncResult)msg.obj; 1102 if (DBG) log("Sim REFRESH with exception: " + ar.exception); 1103 if (ar.exception == null) { 1104 handleSimRefresh((IccRefreshResponse)ar.result); 1105 } 1106 break; 1107 case EVENT_GET_CFIS_DONE: 1108 isRecordLoadResponse = true; 1109 1110 ar = (AsyncResult)msg.obj; 1111 data = (byte[])ar.result; 1112 1113 if (ar.exception != null) { 1114 break; 1115 } 1116 1117 log("EF_CFIS: " + IccUtils.bytesToHexString(data)); 1118 1119 if (validEfCfis(data)) { 1120 mEfCfis = data; 1121 1122 // Refer TS 51.011 Section 10.3.46 for the content description 1123 mCallForwardingEnabled = ((data[1] & 0x01) != 0); 1124 log("EF_CFIS: callForwardingEnabled=" + mCallForwardingEnabled); 1125 1126 mRecordsEventsRegistrants.notifyResult(EVENT_CFI); 1127 } else { 1128 log("EF_CFIS: invalid data=" + IccUtils.bytesToHexString(data)); 1129 } 1130 break; 1131 1132 case EVENT_GET_CSP_CPHS_DONE: 1133 isRecordLoadResponse = true; 1134 1135 ar = (AsyncResult)msg.obj; 1136 1137 if (ar.exception != null) { 1138 loge("Exception in fetching EF_CSP data " + ar.exception); 1139 break; 1140 } 1141 1142 data = (byte[])ar.result; 1143 1144 log("EF_CSP: " + IccUtils.bytesToHexString(data)); 1145 handleEfCspData(data); 1146 break; 1147 1148 case EVENT_GET_GID1_DONE: 1149 isRecordLoadResponse = true; 1150 1151 ar = (AsyncResult)msg.obj; 1152 data =(byte[])ar.result; 1153 1154 if (ar.exception != null) { 1155 loge("Exception in get GID1 " + ar.exception); 1156 mGid1 = null; 1157 break; 1158 } 1159 mGid1 = IccUtils.bytesToHexString(data); 1160 log("GID1: " + mGid1); 1161 1162 break; 1163 1164 default: 1165 super.handleMessage(msg); // IccRecords handles generic record load responses 1166 1167 }}catch (RuntimeException exc) { 1168 // I don't want these exceptions to be fatal 1169 logw("Exception parsing SIM record", exc); 1170 } finally { 1171 // Count up record load responses even if they are fails 1172 if (isRecordLoadResponse) { 1173 onRecordLoaded(); 1174 } 1175 } 1176 } 1177 1178 private void handleFileUpdate(int efid) { 1179 switch(efid) { 1180 case EF_MBDN: 1181 mRecordsToLoad++; 1182 new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6, 1183 mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE)); 1184 break; 1185 case EF_MAILBOX_CPHS: 1186 mRecordsToLoad++; 1187 new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1, 1188 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); 1189 break; 1190 case EF_CSP_CPHS: 1191 mRecordsToLoad++; 1192 log("[CSP] SIM Refresh for EF_CSP_CPHS"); 1193 mFh.loadEFTransparent(EF_CSP_CPHS, 1194 obtainMessage(EVENT_GET_CSP_CPHS_DONE)); 1195 break; 1196 default: 1197 // For now, fetch all records if this is not a 1198 // voicemail number. 1199 // TODO: Handle other cases, instead of fetching all. 1200 mAdnCache.reset(); 1201 fetchSimRecords(); 1202 break; 1203 } 1204 } 1205 1206 private void handleSimRefresh(IccRefreshResponse refreshResponse){ 1207 if (refreshResponse == null) { 1208 if (DBG) log("handleSimRefresh received without input"); 1209 return; 1210 } 1211 1212 if (refreshResponse.aid != null && 1213 !refreshResponse.aid.equals(mParentApp.getAid())) { 1214 // This is for different app. Ignore. 1215 return; 1216 } 1217 1218 switch (refreshResponse.refreshResult) { 1219 case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE: 1220 if (DBG) log("handleSimRefresh with SIM_FILE_UPDATED"); 1221 handleFileUpdate(refreshResponse.efId); 1222 break; 1223 case IccRefreshResponse.REFRESH_RESULT_INIT: 1224 if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT"); 1225 // need to reload all files (that we care about) 1226 onIccRefreshInit(); 1227 break; 1228 case IccRefreshResponse.REFRESH_RESULT_RESET: 1229 if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET"); 1230 mCi.setRadioPower(false, null); 1231 /* Note: no need to call setRadioPower(true). Assuming the desired 1232 * radio power state is still ON (as tracked by ServiceStateTracker), 1233 * ServiceStateTracker will call setRadioPower when it receives the 1234 * RADIO_STATE_CHANGED notification for the power off. And if the 1235 * desired power state has changed in the interim, we don't want to 1236 * override it with an unconditional power on. 1237 */ 1238 mAdnCache.reset(); 1239 break; 1240 default: 1241 // unknown refresh operation 1242 if (DBG) log("handleSimRefresh with unknown operation"); 1243 break; 1244 } 1245 } 1246 1247 /** 1248 * Dispatch 3GPP format message to registrant ({@code GSMPhone} or {@code CDMALTEPhone}) 1249 * to pass to the 3GPP SMS dispatcher for delivery. 1250 */ 1251 private int dispatchGsmMessage(SmsMessage message) { 1252 mNewSmsRegistrants.notifyResult(message); 1253 return 0; 1254 } 1255 1256 private void handleSms(byte[] ba) { 1257 if (ba[0] != 0) 1258 Rlog.d("ENF", "status : " + ba[0]); 1259 1260 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1261 // 3 == "received by MS from network; message to be read" 1262 if (ba[0] == 3) { 1263 int n = ba.length; 1264 1265 // Note: Data may include trailing FF's. That's OK; message 1266 // should still parse correctly. 1267 byte[] pdu = new byte[n - 1]; 1268 System.arraycopy(ba, 1, pdu, 0, n - 1); 1269 SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP); 1270 1271 dispatchGsmMessage(message); 1272 } 1273 } 1274 1275 1276 private void handleSmses(ArrayList<byte[]> messages) { 1277 int count = messages.size(); 1278 1279 for (int i = 0; i < count; i++) { 1280 byte[] ba = messages.get(i); 1281 1282 if (ba[0] != 0) 1283 Rlog.i("ENF", "status " + i + ": " + ba[0]); 1284 1285 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1286 // 3 == "received by MS from network; message to be read" 1287 1288 if (ba[0] == 3) { 1289 int n = ba.length; 1290 1291 // Note: Data may include trailing FF's. That's OK; message 1292 // should still parse correctly. 1293 byte[] pdu = new byte[n - 1]; 1294 System.arraycopy(ba, 1, pdu, 0, n - 1); 1295 SmsMessage message = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP); 1296 1297 dispatchGsmMessage(message); 1298 1299 // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 1300 // 1 == "received by MS from network; message read" 1301 1302 ba[0] = 1; 1303 1304 if (false) { // FIXME: writing seems to crash RdoServD 1305 mFh.updateEFLinearFixed(EF_SMS, 1306 i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i)); 1307 } 1308 } 1309 } 1310 } 1311 1312 @Override 1313 protected void onRecordLoaded() { 1314 // One record loaded successfully or failed, In either case 1315 // we need to update the recordsToLoad count 1316 mRecordsToLoad -= 1; 1317 if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested); 1318 1319 if (mRecordsToLoad == 0 && mRecordsRequested == true) { 1320 onAllRecordsLoaded(); 1321 } else if (mRecordsToLoad < 0) { 1322 loge("recordsToLoad <0, programmer error suspected"); 1323 mRecordsToLoad = 0; 1324 } 1325 } 1326 1327 @Override 1328 protected void onAllRecordsLoaded() { 1329 if (DBG) log("record load complete"); 1330 1331 // Some fields require more than one SIM record to set 1332 1333 String operator = getOperatorNumeric(); 1334 if (!TextUtils.isEmpty(operator)) { 1335 log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" + 1336 operator + "'"); 1337 SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator); 1338 } else { 1339 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping"); 1340 } 1341 1342 if (!TextUtils.isEmpty(mImsi)) { 1343 log("onAllRecordsLoaded set mcc imsi=" + mImsi); 1344 SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, 1345 MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3)))); 1346 } else { 1347 log("onAllRecordsLoaded empty imsi skipping setting mcc"); 1348 } 1349 1350 setVoiceMailByCountry(operator); 1351 setSpnFromConfig(operator); 1352 1353 mRecordsLoadedRegistrants.notifyRegistrants( 1354 new AsyncResult(null, null, null)); 1355 } 1356 1357 //***** Private methods 1358 1359 private void setSpnFromConfig(String carrier) { 1360 if (mSpnOverride.containsCarrier(carrier)) { 1361 mSpn = mSpnOverride.getSpn(carrier); 1362 } 1363 } 1364 1365 1366 private void setVoiceMailByCountry (String spn) { 1367 if (mVmConfig.containsCarrier(spn)) { 1368 mIsVoiceMailFixed = true; 1369 mVoiceMailNum = mVmConfig.getVoiceMailNumber(spn); 1370 mVoiceMailTag = mVmConfig.getVoiceMailTag(spn); 1371 } 1372 } 1373 1374 @Override 1375 public void onReady() { 1376 fetchSimRecords(); 1377 } 1378 1379 protected void fetchSimRecords() { 1380 mRecordsRequested = true; 1381 1382 if (DBG) log("fetchSimRecords " + mRecordsToLoad); 1383 1384 mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); 1385 mRecordsToLoad++; 1386 1387 mFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE)); 1388 mRecordsToLoad++; 1389 1390 // FIXME should examine EF[MSISDN]'s capability configuration 1391 // to determine which is the voice/data/fax line 1392 new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1, 1393 obtainMessage(EVENT_GET_MSISDN_DONE)); 1394 mRecordsToLoad++; 1395 1396 // Record number is subscriber profile 1397 mFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE)); 1398 mRecordsToLoad++; 1399 1400 mFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE)); 1401 mRecordsToLoad++; 1402 1403 // Record number is subscriber profile 1404 mFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE)); 1405 mRecordsToLoad++; 1406 1407 1408 // Also load CPHS-style voice mail indicator, which stores 1409 // the same info as EF[MWIS]. If both exist, both are updated 1410 // but the EF[MWIS] data is preferred 1411 // Please note this must be loaded after EF[MWIS] 1412 mFh.loadEFTransparent( 1413 EF_VOICE_MAIL_INDICATOR_CPHS, 1414 obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE)); 1415 mRecordsToLoad++; 1416 1417 // Same goes for Call Forward Status indicator: fetch both 1418 // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred. 1419 mFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE)); 1420 mRecordsToLoad++; 1421 mFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE)); 1422 mRecordsToLoad++; 1423 1424 1425 getSpnFsm(true, null); 1426 1427 mFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE)); 1428 mRecordsToLoad++; 1429 1430 mFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE)); 1431 mRecordsToLoad++; 1432 1433 mFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE)); 1434 mRecordsToLoad++; 1435 1436 mFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE)); 1437 mRecordsToLoad++; 1438 1439 mFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE)); 1440 mRecordsToLoad++; 1441 1442 mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE)); 1443 mRecordsToLoad++; 1444 1445 // XXX should seek instead of examining them all 1446 if (false) { // XXX 1447 mFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE)); 1448 mRecordsToLoad++; 1449 } 1450 1451 if (CRASH_RIL) { 1452 String sms = "0107912160130310f20404d0110041007030208054832b0120" 1453 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1454 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1455 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1456 + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1457 + "ffffffffffffffffffffffffffffff"; 1458 byte[] ba = IccUtils.hexStringToBytes(sms); 1459 1460 mFh.updateEFLinearFixed(EF_SMS, 1, ba, null, 1461 obtainMessage(EVENT_MARK_SMS_READ_DONE, 1)); 1462 } 1463 if (DBG) log("fetchSimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested); 1464 } 1465 1466 /** 1467 * Returns the SpnDisplayRule based on settings on the SIM and the 1468 * specified plmn (currently-registered PLMN). See TS 22.101 Annex A 1469 * and TS 51.011 10.3.11 for details. 1470 * 1471 * If the SPN is not found on the SIM or is empty, the rule is 1472 * always PLMN_ONLY. 1473 */ 1474 @Override 1475 public int getDisplayRule(String plmn) { 1476 int rule; 1477 if (TextUtils.isEmpty(mSpn) || mSpnDisplayCondition == -1) { 1478 // No EF_SPN content was found on the SIM, or not yet loaded. Just show ONS. 1479 rule = SPN_RULE_SHOW_PLMN; 1480 } else if (isOnMatchingPlmn(plmn)) { 1481 rule = SPN_RULE_SHOW_SPN; 1482 if ((mSpnDisplayCondition & 0x01) == 0x01) { 1483 // ONS required when registered to HPLMN or PLMN in EF_SPDI 1484 rule |= SPN_RULE_SHOW_PLMN; 1485 } 1486 } else { 1487 rule = SPN_RULE_SHOW_PLMN; 1488 if ((mSpnDisplayCondition & 0x02) == 0x00) { 1489 // SPN required if not registered to HPLMN or PLMN in EF_SPDI 1490 rule |= SPN_RULE_SHOW_SPN; 1491 } 1492 } 1493 return rule; 1494 } 1495 1496 /** 1497 * Checks if plmn is HPLMN or on the spdiNetworks list. 1498 */ 1499 private boolean isOnMatchingPlmn(String plmn) { 1500 if (plmn == null) return false; 1501 1502 if (plmn.equals(getOperatorNumeric())) { 1503 return true; 1504 } 1505 1506 if (mSpdiNetworks != null) { 1507 for (String spdiNet : mSpdiNetworks) { 1508 if (plmn.equals(spdiNet)) { 1509 return true; 1510 } 1511 } 1512 } 1513 return false; 1514 } 1515 1516 /** 1517 * States of Get SPN Finite State Machine which only used by getSpnFsm() 1518 */ 1519 private enum GetSpnFsmState { 1520 IDLE, // No initialized 1521 INIT, // Start FSM 1522 READ_SPN_3GPP, // Load EF_SPN firstly 1523 READ_SPN_CPHS, // Load EF_SPN_CPHS secondly 1524 READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last 1525 } 1526 1527 /** 1528 * Finite State Machine to load Service Provider Name , which can be stored 1529 * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2) 1530 * 1531 * After starting, FSM will search SPN EFs in order and stop after finding 1532 * the first valid SPN 1533 * 1534 * If the FSM gets restart while waiting for one of 1535 * SPN EFs results (i.e. a SIM refresh occurs after issuing 1536 * read EF_CPHS_SPN), it will re-initialize only after 1537 * receiving and discarding the unfinished SPN EF result. 1538 * 1539 * @param start set true only for initialize loading 1540 * @param ar the AsyncResult from loadEFTransparent 1541 * ar.exception holds exception in error 1542 * ar.result is byte[] for data in success 1543 */ 1544 private void getSpnFsm(boolean start, AsyncResult ar) { 1545 byte[] data; 1546 1547 if (start) { 1548 // Check previous state to see if there is outstanding 1549 // SPN read 1550 if(mSpnState == GetSpnFsmState.READ_SPN_3GPP || 1551 mSpnState == GetSpnFsmState.READ_SPN_CPHS || 1552 mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS || 1553 mSpnState == GetSpnFsmState.INIT) { 1554 // Set INIT then return so the INIT code 1555 // will run when the outstanding read done. 1556 mSpnState = GetSpnFsmState.INIT; 1557 return; 1558 } else { 1559 mSpnState = GetSpnFsmState.INIT; 1560 } 1561 } 1562 1563 switch(mSpnState){ 1564 case INIT: 1565 mSpn = null; 1566 1567 mFh.loadEFTransparent(EF_SPN, 1568 obtainMessage(EVENT_GET_SPN_DONE)); 1569 mRecordsToLoad++; 1570 1571 mSpnState = GetSpnFsmState.READ_SPN_3GPP; 1572 break; 1573 case READ_SPN_3GPP: 1574 if (ar != null && ar.exception == null) { 1575 data = (byte[]) ar.result; 1576 mSpnDisplayCondition = 0xff & data[0]; 1577 mSpn = IccUtils.adnStringFieldToString(data, 1, data.length - 1); 1578 1579 if (DBG) log("Load EF_SPN: " + mSpn 1580 + " spnDisplayCondition: " + mSpnDisplayCondition); 1581 SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn); 1582 1583 mSpnState = GetSpnFsmState.IDLE; 1584 } else { 1585 mFh.loadEFTransparent( EF_SPN_CPHS, 1586 obtainMessage(EVENT_GET_SPN_DONE)); 1587 mRecordsToLoad++; 1588 1589 mSpnState = GetSpnFsmState.READ_SPN_CPHS; 1590 1591 // See TS 51.011 10.3.11. Basically, default to 1592 // show PLMN always, and SPN also if roaming. 1593 mSpnDisplayCondition = -1; 1594 } 1595 break; 1596 case READ_SPN_CPHS: 1597 if (ar != null && ar.exception == null) { 1598 data = (byte[]) ar.result; 1599 mSpn = IccUtils.adnStringFieldToString(data, 0, data.length); 1600 1601 if (DBG) log("Load EF_SPN_CPHS: " + mSpn); 1602 SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn); 1603 1604 mSpnState = GetSpnFsmState.IDLE; 1605 } else { 1606 mFh.loadEFTransparent( 1607 EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE)); 1608 mRecordsToLoad++; 1609 1610 mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS; 1611 } 1612 break; 1613 case READ_SPN_SHORT_CPHS: 1614 if (ar != null && ar.exception == null) { 1615 data = (byte[]) ar.result; 1616 mSpn = IccUtils.adnStringFieldToString(data, 0, data.length); 1617 1618 if (DBG) log("Load EF_SPN_SHORT_CPHS: " + mSpn); 1619 SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn); 1620 }else { 1621 if (DBG) log("No SPN loaded in either CHPS or 3GPP"); 1622 } 1623 1624 mSpnState = GetSpnFsmState.IDLE; 1625 break; 1626 default: 1627 mSpnState = GetSpnFsmState.IDLE; 1628 } 1629 } 1630 1631 /** 1632 * Parse TS 51.011 EF[SPDI] record 1633 * This record contains the list of numeric network IDs that 1634 * are treated specially when determining SPN display 1635 */ 1636 private void 1637 parseEfSpdi(byte[] data) { 1638 SimTlv tlv = new SimTlv(data, 0, data.length); 1639 1640 byte[] plmnEntries = null; 1641 1642 for ( ; tlv.isValidObject() ; tlv.nextObject()) { 1643 // Skip SPDI tag, if existant 1644 if (tlv.getTag() == TAG_SPDI) { 1645 tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length); 1646 } 1647 // There should only be one TAG_SPDI_PLMN_LIST 1648 if (tlv.getTag() == TAG_SPDI_PLMN_LIST) { 1649 plmnEntries = tlv.getData(); 1650 break; 1651 } 1652 } 1653 1654 if (plmnEntries == null) { 1655 return; 1656 } 1657 1658 mSpdiNetworks = new ArrayList<String>(plmnEntries.length / 3); 1659 1660 for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) { 1661 String plmnCode; 1662 plmnCode = IccUtils.bcdToString(plmnEntries, i, 3); 1663 1664 // Valid operator codes are 5 or 6 digits 1665 if (plmnCode.length() >= 5) { 1666 log("EF_SPDI network: " + plmnCode); 1667 mSpdiNetworks.add(plmnCode); 1668 } 1669 } 1670 } 1671 1672 /** 1673 * check to see if Mailbox Number is allocated and activated in CPHS SST 1674 */ 1675 private boolean isCphsMailboxEnabled() { 1676 if (mCphsInfo == null) return false; 1677 return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED ); 1678 } 1679 1680 @Override 1681 protected void log(String s) { 1682 Rlog.d(LOG_TAG, "[SIMRecords] " + s); 1683 } 1684 1685 @Override 1686 protected void loge(String s) { 1687 Rlog.e(LOG_TAG, "[SIMRecords] " + s); 1688 } 1689 1690 protected void logw(String s, Throwable tr) { 1691 Rlog.w(LOG_TAG, "[SIMRecords] " + s, tr); 1692 } 1693 1694 protected void logv(String s) { 1695 Rlog.v(LOG_TAG, "[SIMRecords] " + s); 1696 } 1697 1698 /** 1699 * Return true if "Restriction of menu options for manual PLMN selection" 1700 * bit is set or EF_CSP data is unavailable, return false otherwise. 1701 */ 1702 @Override 1703 public boolean isCspPlmnEnabled() { 1704 return mCspPlmnEnabled; 1705 } 1706 1707 /** 1708 * Parse EF_CSP data and check if 1709 * "Restriction of menu options for manual PLMN selection" is 1710 * Enabled/Disabled 1711 * 1712 * @param data EF_CSP hex data. 1713 */ 1714 private void handleEfCspData(byte[] data) { 1715 // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined 1716 // 18 bytes (i.e 9 service groups info) and additional data specific to 1717 // operator. The valueAddedServicesGroup is not part of standard 1718 // services. This is operator specific and can be programmed any where. 1719 // Normally this is programmed as 10th service after the standard 1720 // services. 1721 int usedCspGroups = data.length / 2; 1722 // This is the "Service Group Number" of "Value Added Services Group". 1723 byte valueAddedServicesGroup = (byte)0xC0; 1724 1725 mCspPlmnEnabled = true; 1726 for (int i = 0; i < usedCspGroups; i++) { 1727 if (data[2 * i] == valueAddedServicesGroup) { 1728 log("[CSP] found ValueAddedServicesGroup, value " + data[(2 * i) + 1]); 1729 if ((data[(2 * i) + 1] & 0x80) == 0x80) { 1730 // Bit 8 is for 1731 // "Restriction of menu options for manual PLMN selection". 1732 // Operator Selection menu should be enabled. 1733 mCspPlmnEnabled = true; 1734 } else { 1735 mCspPlmnEnabled = false; 1736 // Operator Selection menu should be disabled. 1737 // Operator Selection Mode should be set to Automatic. 1738 log("[CSP] Set Automatic Network Selection"); 1739 mNetworkSelectionModeAutomaticRegistrants.notifyRegistrants(); 1740 } 1741 return; 1742 } 1743 } 1744 1745 log("[CSP] Value Added Service Group (0xC0), not found!"); 1746 } 1747 1748 @Override 1749 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1750 pw.println("SIMRecords: " + this); 1751 pw.println(" extends:"); 1752 super.dump(fd, pw, args); 1753 pw.println(" mVmConfig=" + mVmConfig); 1754 pw.println(" mSpnOverride=" + mSpnOverride); 1755 pw.println(" mCallForwardingEnabled=" + mCallForwardingEnabled); 1756 pw.println(" mSpnState=" + mSpnState); 1757 pw.println(" mCphsInfo=" + mCphsInfo); 1758 pw.println(" mCspPlmnEnabled=" + mCspPlmnEnabled); 1759 pw.println(" mEfMWIS[]=" + Arrays.toString(mEfMWIS)); 1760 pw.println(" mEfCPHS_MWI[]=" + Arrays.toString(mEfCPHS_MWI)); 1761 pw.println(" mEfCff[]=" + Arrays.toString(mEfCff)); 1762 pw.println(" mEfCfis[]=" + Arrays.toString(mEfCfis)); 1763 pw.println(" mSpnDisplayCondition=" + mSpnDisplayCondition); 1764 pw.println(" mSpdiNetworks[]=" + mSpdiNetworks); 1765 pw.println(" mPnnHomeName=" + mPnnHomeName); 1766 pw.println(" mUsimServiceTable=" + mUsimServiceTable); 1767 pw.println(" mGid1=" + mGid1); 1768 pw.flush(); 1769 } 1770 } 1771