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