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