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