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