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