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