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