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