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