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