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