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