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