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