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