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