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