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