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