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