RuimRecords.java revision 0cdae27a3afaf241764dc3a03bfff6ed012656db
1/* 2 * Copyright (C) 2008 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.uicc; 18 19 20import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; 21import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; 22 23import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; 24import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM; 25 26import java.io.FileDescriptor; 27import java.io.PrintWriter; 28import java.util.ArrayList; 29import java.util.Arrays; 30import java.util.Locale; 31import android.content.Context; 32import android.os.AsyncResult; 33import android.os.Message; 34import android.os.SystemProperties; 35import android.telephony.TelephonyManager; 36import android.telephony.Rlog; 37import android.text.TextUtils; 38 39import com.android.internal.telephony.CommandsInterface; 40import com.android.internal.telephony.GsmAlphabet; 41import com.android.internal.telephony.MccTable; 42 43import com.android.internal.telephony.cdma.sms.UserData; 44import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; 45 46 47/** 48 * {@hide} 49 */ 50public final class RuimRecords extends IccRecords { 51 static final String LOG_TAG = "RuimRecords"; 52 53 private boolean mOtaCommited=false; 54 55 // ***** Instance Variables 56 57 private String mMyMobileNumber; 58 private String mMin2Min1; 59 60 private String mPrlVersion; 61 // From CSIM application 62 private byte[] mEFpl = null; 63 private byte[] mEFli = null; 64 boolean mCsimSpnDisplayCondition = false; 65 private String mMdn; 66 private String mMin; 67 private String mHomeSystemId; 68 private String mHomeNetworkId; 69 70 @Override 71 public String toString() { 72 return "RuimRecords: " + super.toString() 73 + " m_ota_commited" + mOtaCommited 74 + " mMyMobileNumber=" + "xxxx" 75 + " mMin2Min1=" + mMin2Min1 76 + " mPrlVersion=" + mPrlVersion 77 + " mEFpl=" + mEFpl 78 + " mEFli=" + mEFli 79 + " mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition 80 + " mMdn=" + mMdn 81 + " mMin=" + mMin 82 + " mHomeSystemId=" + mHomeSystemId 83 + " mHomeNetworkId=" + mHomeNetworkId; 84 } 85 86 // ***** Event Constants 87 private static final int EVENT_GET_IMSI_DONE = 3; 88 private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4; 89 private static final int EVENT_GET_ICCID_DONE = 5; 90 private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10; 91 private static final int EVENT_UPDATE_DONE = 14; 92 private static final int EVENT_GET_SST_DONE = 17; 93 private static final int EVENT_GET_ALL_SMS_DONE = 18; 94 private static final int EVENT_MARK_SMS_READ_DONE = 19; 95 96 private static final int EVENT_SMS_ON_RUIM = 21; 97 private static final int EVENT_GET_SMS_DONE = 22; 98 99 private static final int EVENT_RUIM_REFRESH = 31; 100 101 public RuimRecords(UiccCardApplication app, Context c, CommandsInterface ci) { 102 super(app, c, ci); 103 104 mAdnCache = new AdnRecordCache(mFh); 105 106 mRecordsRequested = false; // No load request is made till SIM ready 107 108 // recordsToLoad is set to 0 because no requests are made yet 109 mRecordsToLoad = 0; 110 111 // NOTE the EVENT_SMS_ON_RUIM is not registered 112 mCi.registerForIccRefresh(this, EVENT_RUIM_REFRESH, null); 113 114 // Start off by setting empty state 115 resetRecords(); 116 117 mParentApp.registerForReady(this, EVENT_APP_READY, null); 118 if (DBG) log("RuimRecords X ctor this=" + this); 119 } 120 121 @Override 122 public void dispose() { 123 if (DBG) log("Disposing RuimRecords " + this); 124 //Unregister for all events 125 mCi.unregisterForIccRefresh(this); 126 mParentApp.unregisterForReady(this); 127 resetRecords(); 128 super.dispose(); 129 } 130 131 @Override 132 protected void finalize() { 133 if(DBG) log("RuimRecords finalized"); 134 } 135 136 protected void resetRecords() { 137 mCountVoiceMessages = 0; 138 mMncLength = UNINITIALIZED; 139 log("setting0 mMncLength" + mMncLength); 140 mIccId = null; 141 142 mAdnCache.reset(); 143 144 // Don't clean up PROPERTY_ICC_OPERATOR_ISO_COUNTRY and 145 // PROPERTY_ICC_OPERATOR_NUMERIC here. Since not all CDMA 146 // devices have RUIM, these properties should keep the original 147 // values, e.g. build time settings, when there is no RUIM but 148 // set new values when RUIM is available and loaded. 149 150 // recordsRequested is set to false indicating that the SIM 151 // read requests made so far are not valid. This is set to 152 // true only when fresh set of read requests are made. 153 mRecordsRequested = false; 154 } 155 156 @Override 157 public String getIMSI() { 158 return mImsi; 159 } 160 161 public String getMdnNumber() { 162 return mMyMobileNumber; 163 } 164 165 public String getCdmaMin() { 166 return mMin2Min1; 167 } 168 169 /** Returns null if RUIM is not yet ready */ 170 public String getPrlVersion() { 171 return mPrlVersion; 172 } 173 174 @Override 175 public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){ 176 // In CDMA this is Operator/OEM dependent 177 AsyncResult.forMessage((onComplete)).exception = 178 new IccException("setVoiceMailNumber not implemented"); 179 onComplete.sendToTarget(); 180 loge("method setVoiceMailNumber is not implemented"); 181 } 182 183 /** 184 * Called by CCAT Service when REFRESH is received. 185 * @param fileChanged indicates whether any files changed 186 * @param fileList if non-null, a list of EF files that changed 187 */ 188 @Override 189 public void onRefresh(boolean fileChanged, int[] fileList) { 190 if (fileChanged) { 191 // A future optimization would be to inspect fileList and 192 // only reload those files that we care about. For now, 193 // just re-fetch all RUIM records that we cache. 194 fetchRuimRecords(); 195 } 196 } 197 198 private int adjstMinDigits (int digits) { 199 // Per C.S0005 section 2.3.1. 200 digits += 111; 201 digits = (digits % 10 == 0)?(digits - 10):digits; 202 digits = ((digits / 10) % 10 == 0)?(digits - 100):digits; 203 digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits; 204 return digits; 205 } 206 207 /** 208 * Returns the 5 or 6 digit MCC/MNC of the operator that 209 * provided the RUIM card. Returns null of RUIM is not yet ready 210 */ 211 public String getRUIMOperatorNumeric() { 212 if (mImsi == null) { 213 return null; 214 } 215 216 if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) { 217 // Length = length of MCC + length of MNC 218 // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3) 219 return mImsi.substring(0, 3 + mMncLength); 220 } 221 222 // Guess the MNC length based on the MCC if we don't 223 // have a valid value in ef[ad] 224 225 int mcc = Integer.parseInt(mImsi.substring(0,3)); 226 return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc)); 227 } 228 229 // Refer to ETSI TS 102.221 230 private class EfPlLoaded implements IccRecordLoaded { 231 @Override 232 public String getEfName() { 233 return "EF_PL"; 234 } 235 236 @Override 237 public void onRecordLoaded(AsyncResult ar) { 238 mEFpl = (byte[]) ar.result; 239 if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl)); 240 } 241 } 242 243 // Refer to C.S0065 5.2.26 244 private class EfCsimLiLoaded implements IccRecordLoaded { 245 @Override 246 public String getEfName() { 247 return "EF_CSIM_LI"; 248 } 249 250 @Override 251 public void onRecordLoaded(AsyncResult ar) { 252 mEFli = (byte[]) ar.result; 253 // convert csim efli data to iso 639 format 254 for (int i = 0; i < mEFli.length; i+=2) { 255 switch(mEFli[i+1]) { 256 case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break; 257 case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break; 258 case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break; 259 case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break; 260 case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break; 261 case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break; 262 case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break; 263 default: mEFli[i] = ' '; mEFli[i+1] = ' '; 264 } 265 } 266 267 if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli)); 268 } 269 } 270 271 // Refer to C.S0065 5.2.32 272 private class EfCsimSpnLoaded implements IccRecordLoaded { 273 @Override 274 public String getEfName() { 275 return "EF_CSIM_SPN"; 276 } 277 278 @Override 279 public void onRecordLoaded(AsyncResult ar) { 280 byte[] data = (byte[]) ar.result; 281 if (DBG) log("CSIM_SPN=" + 282 IccUtils.bytesToHexString(data)); 283 284 // C.S0065 for EF_SPN decoding 285 mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0); 286 287 int encoding = data[1]; 288 int language = data[2]; 289 byte[] spnData = new byte[32]; 290 int len = ((data.length - 3) < 32) ? (data.length - 3) : 32; 291 System.arraycopy(data, 3, spnData, 0, len); 292 293 int numBytes; 294 for (numBytes = 0; numBytes < spnData.length; numBytes++) { 295 if ((spnData[numBytes] & 0xFF) == 0xFF) break; 296 } 297 298 if (numBytes == 0) { 299 setServiceProviderName(""); 300 return; 301 } 302 try { 303 switch (encoding) { 304 case UserData.ENCODING_OCTET: 305 case UserData.ENCODING_LATIN: 306 setServiceProviderName(new String(spnData, 0, numBytes, "ISO-8859-1")); 307 break; 308 case UserData.ENCODING_IA5: 309 case UserData.ENCODING_GSM_7BIT_ALPHABET: 310 setServiceProviderName( 311 GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7)); 312 break; 313 case UserData.ENCODING_7BIT_ASCII: 314 setServiceProviderName(new String(spnData, 0, numBytes, "US-ASCII")); 315 break; 316 case UserData.ENCODING_UNICODE_16: 317 setServiceProviderName(new String(spnData, 0, numBytes, "utf-16")); 318 break; 319 default: 320 log("SPN encoding not supported"); 321 } 322 } catch(Exception e) { 323 log("spn decode error: " + e); 324 } 325 if (DBG) log("spn=" + getServiceProviderName()); 326 if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition); 327 SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName()); 328 } 329 } 330 331 private class EfCsimMdnLoaded implements IccRecordLoaded { 332 @Override 333 public String getEfName() { 334 return "EF_CSIM_MDN"; 335 } 336 337 @Override 338 public void onRecordLoaded(AsyncResult ar) { 339 byte[] data = (byte[]) ar.result; 340 if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data)); 341 // Refer to C.S0065 5.2.35 342 int mdnDigitsNum = 0x0F & data[0]; 343 mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum); 344 if (DBG) log("CSIM MDN=" + mMdn); 345 } 346 } 347 348 private class EfCsimImsimLoaded implements IccRecordLoaded { 349 @Override 350 public String getEfName() { 351 return "EF_CSIM_IMSIM"; 352 } 353 354 @Override 355 public void onRecordLoaded(AsyncResult ar) { 356 byte[] data = (byte[]) ar.result; 357 if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data)); 358 // C.S0065 section 5.2.2 for IMSI_M encoding 359 // C.S0005 section 2.3.1 for MIN encoding in IMSI_M. 360 boolean provisioned = ((data[7] & 0x80) == 0x80); 361 362 if (provisioned) { 363 int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]); 364 int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6; 365 int digit7 = 0x0F & (data[4] >> 2); 366 if (digit7 > 0x09) digit7 = 0; 367 int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]); 368 first3digits = adjstMinDigits(first3digits); 369 second3digits = adjstMinDigits(second3digits); 370 last3digits = adjstMinDigits(last3digits); 371 372 StringBuilder builder = new StringBuilder(); 373 builder.append(String.format(Locale.US, "%03d", first3digits)); 374 builder.append(String.format(Locale.US, "%03d", second3digits)); 375 builder.append(String.format(Locale.US, "%d", digit7)); 376 builder.append(String.format(Locale.US, "%03d", last3digits)); 377 mMin = builder.toString(); 378 if (DBG) log("min present=" + mMin); 379 } else { 380 if (DBG) log("min not present"); 381 } 382 } 383 } 384 385 private class EfCsimCdmaHomeLoaded implements IccRecordLoaded { 386 @Override 387 public String getEfName() { 388 return "EF_CSIM_CDMAHOME"; 389 } 390 391 @Override 392 public void onRecordLoaded(AsyncResult ar) { 393 // Per C.S0065 section 5.2.8 394 ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result; 395 if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size()); 396 if (dataList.isEmpty()) { 397 return; 398 } 399 StringBuilder sidBuf = new StringBuilder(); 400 StringBuilder nidBuf = new StringBuilder(); 401 402 for (byte[] data : dataList) { 403 if (data.length == 5) { 404 int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF); 405 int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF); 406 sidBuf.append(sid).append(','); 407 nidBuf.append(nid).append(','); 408 } 409 } 410 // remove trailing "," 411 sidBuf.setLength(sidBuf.length()-1); 412 nidBuf.setLength(nidBuf.length()-1); 413 414 mHomeSystemId = sidBuf.toString(); 415 mHomeNetworkId = nidBuf.toString(); 416 } 417 } 418 419 private class EfCsimEprlLoaded implements IccRecordLoaded { 420 @Override 421 public String getEfName() { 422 return "EF_CSIM_EPRL"; 423 } 424 @Override 425 public void onRecordLoaded(AsyncResult ar) { 426 onGetCSimEprlDone(ar); 427 } 428 } 429 430 private void onGetCSimEprlDone(AsyncResult ar) { 431 // C.S0065 section 5.2.57 for EFeprl encoding 432 // C.S0016 section 3.5.5 for PRL format. 433 byte[] data = (byte[]) ar.result; 434 if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data)); 435 436 // Only need the first 4 bytes of record 437 if (data.length > 3) { 438 int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF); 439 mPrlVersion = Integer.toString(prlId); 440 } 441 if (DBG) log("CSIM PRL version=" + mPrlVersion); 442 } 443 444 @Override 445 public void handleMessage(Message msg) { 446 AsyncResult ar; 447 448 byte data[]; 449 450 boolean isRecordLoadResponse = false; 451 452 if (mDestroyed.get()) { 453 loge("Received message " + msg + 454 "[" + msg.what + "] while being destroyed. Ignoring."); 455 return; 456 } 457 458 try { switch (msg.what) { 459 case EVENT_APP_READY: 460 onReady(); 461 break; 462 463 case EVENT_GET_DEVICE_IDENTITY_DONE: 464 log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received"); 465 break; 466 467 /* IO events */ 468 case EVENT_GET_IMSI_DONE: 469 isRecordLoadResponse = true; 470 471 ar = (AsyncResult)msg.obj; 472 if (ar.exception != null) { 473 loge("Exception querying IMSI, Exception:" + ar.exception); 474 break; 475 } 476 477 mImsi = (String) ar.result; 478 479 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more 480 // than 15 (and usually 15). 481 if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) { 482 loge("invalid IMSI " + mImsi); 483 mImsi = null; 484 } 485 486 // FIXME: CSIM IMSI may not contain the MNC. 487 if (false) { 488 log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx"); 489 490 String operatorNumeric = getRUIMOperatorNumeric(); 491 if (operatorNumeric != null) { 492 if (operatorNumeric.length() <= 6) { 493 log("update mccmnc=" + operatorNumeric); 494 MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); 495 } 496 } 497 } else { 498 String operatorNumeric = getRUIMOperatorNumeric(); 499 log("NO update mccmnc=" + operatorNumeric); 500 } 501 502 break; 503 504 case EVENT_GET_CDMA_SUBSCRIPTION_DONE: 505 ar = (AsyncResult)msg.obj; 506 String localTemp[] = (String[])ar.result; 507 if (ar.exception != null) { 508 break; 509 } 510 511 mMyMobileNumber = localTemp[0]; 512 mMin2Min1 = localTemp[3]; 513 mPrlVersion = localTemp[4]; 514 515 log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1); 516 517 break; 518 519 case EVENT_GET_ICCID_DONE: 520 isRecordLoadResponse = true; 521 522 ar = (AsyncResult)msg.obj; 523 data = (byte[])ar.result; 524 525 if (ar.exception != null) { 526 break; 527 } 528 529 mIccId = IccUtils.bcdToString(data, 0, data.length); 530 531 log("iccid: " + mIccId); 532 533 break; 534 535 case EVENT_UPDATE_DONE: 536 ar = (AsyncResult)msg.obj; 537 if (ar.exception != null) { 538 Rlog.i(LOG_TAG, "RuimRecords update failed", ar.exception); 539 } 540 break; 541 542 case EVENT_GET_ALL_SMS_DONE: 543 case EVENT_MARK_SMS_READ_DONE: 544 case EVENT_SMS_ON_RUIM: 545 case EVENT_GET_SMS_DONE: 546 Rlog.w(LOG_TAG, "Event not supported: " + msg.what); 547 break; 548 549 // TODO: probably EF_CST should be read instead 550 case EVENT_GET_SST_DONE: 551 log("Event EVENT_GET_SST_DONE Received"); 552 break; 553 554 case EVENT_RUIM_REFRESH: 555 isRecordLoadResponse = false; 556 ar = (AsyncResult)msg.obj; 557 if (ar.exception == null) { 558 handleRuimRefresh((IccRefreshResponse)ar.result); 559 } 560 break; 561 562 default: 563 super.handleMessage(msg); // IccRecords handles generic record load responses 564 565 }}catch (RuntimeException exc) { 566 // I don't want these exceptions to be fatal 567 Rlog.w(LOG_TAG, "Exception parsing RUIM record", exc); 568 } finally { 569 // Count up record load responses even if they are fails 570 if (isRecordLoadResponse) { 571 onRecordLoaded(); 572 } 573 } 574 } 575 576 /** 577 * Returns an array of languages we have assets for. 578 * 579 * NOTE: This array will have duplicates. If this method will be caused 580 * frequently or in a tight loop, it can be rewritten for efficiency. 581 */ 582 private static String[] getAssetLanguages(Context ctx) { 583 final String[] locales = ctx.getAssets().getLocales(); 584 final String[] localeLangs = new String[locales.length]; 585 for (int i = 0; i < locales.length; ++i) { 586 final String localeStr = locales[i]; 587 final int separator = localeStr.indexOf('-'); 588 if (separator < 0) { 589 localeLangs[i] = localeStr; 590 } else { 591 localeLangs[i] = localeStr.substring(0, separator); 592 } 593 } 594 595 return localeLangs; 596 } 597 598 private String findBestLanguage(byte[] languages) { 599 final String[] assetLanguages = getAssetLanguages(mContext); 600 601 if ((languages == null) || (assetLanguages == null)) return null; 602 603 // Each 2-bytes consists of one language 604 for (int i = 0; (i + 1) < languages.length; i += 2) { 605 final String lang; 606 try { 607 lang = new String(languages, i, 2, "ISO-8859-1"); 608 } catch(java.io.UnsupportedEncodingException e) { 609 log("Failed to parse SIM language records"); 610 continue; 611 } 612 613 for (int j = 0; j < assetLanguages.length; j++) { 614 if (assetLanguages[j].equals(lang)) { 615 return lang; 616 } 617 } 618 } 619 620 // no match found. return null 621 return null; 622 } 623 624 private void setLocaleFromCsim() { 625 String prefLang = null; 626 // check EFli then EFpl 627 prefLang = findBestLanguage(mEFli); 628 629 if (prefLang == null) { 630 prefLang = findBestLanguage(mEFpl); 631 } 632 633 if (prefLang != null) { 634 // check country code from SIM 635 String imsi = getIMSI(); 636 String country = null; 637 if (imsi != null) { 638 country = MccTable.countryCodeForMcc( 639 Integer.parseInt(imsi.substring(0,3))); 640 } 641 log("Setting locale to " + prefLang + "_" + country); 642 MccTable.setSystemLocale(mContext, prefLang, country); 643 } else { 644 log ("No suitable CSIM selected locale"); 645 } 646 } 647 648 @Override 649 protected void onRecordLoaded() { 650 // One record loaded successfully or failed, In either case 651 // we need to update the recordsToLoad count 652 mRecordsToLoad -= 1; 653 if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested); 654 655 if (mRecordsToLoad == 0 && mRecordsRequested == true) { 656 onAllRecordsLoaded(); 657 } else if (mRecordsToLoad < 0) { 658 loge("recordsToLoad <0, programmer error suspected"); 659 mRecordsToLoad = 0; 660 } 661 } 662 663 @Override 664 protected void onAllRecordsLoaded() { 665 if (DBG) log("record load complete"); 666 667 // Further records that can be inserted are Operator/OEM dependent 668 669 // FIXME: CSIM IMSI may not contain the MNC. 670 if (false) { 671 String operator = getRUIMOperatorNumeric(); 672 if (!TextUtils.isEmpty(operator)) { 673 log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" + 674 operator + "'"); 675 log("update icc_operator_numeric=" + operator); 676 SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator); 677 } else { 678 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping"); 679 } 680 681 if (!TextUtils.isEmpty(mImsi)) { 682 log("onAllRecordsLoaded set mcc imsi=" + mImsi); 683 SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, 684 MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3)))); 685 } else { 686 log("onAllRecordsLoaded empty imsi skipping setting mcc"); 687 } 688 } 689 690 setLocaleFromCsim(); 691 mRecordsLoadedRegistrants.notifyRegistrants( 692 new AsyncResult(null, null, null)); 693 } 694 695 @Override 696 public void onReady() { 697 fetchRuimRecords(); 698 699 mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); 700 } 701 702 703 private void fetchRuimRecords() { 704 mRecordsRequested = true; 705 706 if (DBG) log("fetchRuimRecords " + mRecordsToLoad); 707 708 mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); 709 mRecordsToLoad++; 710 711 mFh.loadEFTransparent(EF_ICCID, 712 obtainMessage(EVENT_GET_ICCID_DONE)); 713 mRecordsToLoad++; 714 715 mFh.loadEFTransparent(EF_PL, 716 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded())); 717 mRecordsToLoad++; 718 719 mFh.loadEFTransparent(EF_CSIM_LI, 720 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded())); 721 mRecordsToLoad++; 722 723 mFh.loadEFTransparent(EF_CSIM_SPN, 724 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded())); 725 mRecordsToLoad++; 726 727 mFh.loadEFLinearFixed(EF_CSIM_MDN, 1, 728 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded())); 729 mRecordsToLoad++; 730 731 mFh.loadEFTransparent(EF_CSIM_IMSIM, 732 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded())); 733 mRecordsToLoad++; 734 735 mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME, 736 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded())); 737 mRecordsToLoad++; 738 739 // Entire PRL could be huge. We are only interested in 740 // the first 4 bytes of the record. 741 mFh.loadEFTransparent(EF_CSIM_EPRL, 4, 742 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded())); 743 mRecordsToLoad++; 744 745 if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested); 746 // Further records that can be inserted are Operator/OEM dependent 747 } 748 749 /** 750 * {@inheritDoc} 751 * 752 * No Display rule for RUIMs yet. 753 */ 754 @Override 755 public int getDisplayRule(String plmn) { 756 // TODO together with spn 757 return 0; 758 } 759 760 @Override 761 public boolean isProvisioned() { 762 // If UICC card has CSIM app, look for MDN and MIN field 763 // to determine if the SIM is provisioned. Otherwise, 764 // consider the SIM is provisioned. (for case of ordinal 765 // USIM only UICC.) 766 // If PROPERTY_TEST_CSIM is defined, bypess provision check 767 // and consider the SIM is provisioned. 768 if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) { 769 return true; 770 } 771 772 if (mParentApp == null) { 773 return false; 774 } 775 776 if (mParentApp.getType() == AppType.APPTYPE_CSIM && 777 ((mMdn == null) || (mMin == null))) { 778 return false; 779 } 780 return true; 781 } 782 783 @Override 784 public void setVoiceMessageWaiting(int line, int countWaiting) { 785 if (line != 1) { 786 // only profile 1 is supported 787 return; 788 } 789 790 // range check 791 if (countWaiting < 0) { 792 countWaiting = -1; 793 } else if (countWaiting > 0xff) { 794 // C.S0015-B v2, 4.5.12 795 // range: 0-99 796 countWaiting = 0xff; 797 } 798 mCountVoiceMessages = countWaiting; 799 800 mRecordsEventsRegistrants.notifyResult(EVENT_MWI); 801 } 802 803 private void handleRuimRefresh(IccRefreshResponse refreshResponse) { 804 if (refreshResponse == null) { 805 if (DBG) log("handleRuimRefresh received without input"); 806 return; 807 } 808 809 if (refreshResponse.aid != null && 810 !refreshResponse.aid.equals(mParentApp.getAid())) { 811 // This is for different app. Ignore. 812 return; 813 } 814 815 switch (refreshResponse.refreshResult) { 816 case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE: 817 if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); 818 mAdnCache.reset(); 819 fetchRuimRecords(); 820 break; 821 case IccRefreshResponse.REFRESH_RESULT_INIT: 822 if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); 823 // need to reload all files (that we care about) 824 onIccRefreshInit(); 825 break; 826 case IccRefreshResponse.REFRESH_RESULT_RESET: 827 if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); 828 mCi.setRadioPower(false, null); 829 /* Note: no need to call setRadioPower(true). Assuming the desired 830 * radio power state is still ON (as tracked by ServiceStateTracker), 831 * ServiceStateTracker will call setRadioPower when it receives the 832 * RADIO_STATE_CHANGED notification for the power off. And if the 833 * desired power state has changed in the interim, we don't want to 834 * override it with an unconditional power on. 835 */ 836 break; 837 default: 838 // unknown refresh operation 839 if (DBG) log("handleRuimRefresh with unknown operation"); 840 break; 841 } 842 } 843 844 public String getMdn() { 845 return mMdn; 846 } 847 848 public String getMin() { 849 return mMin; 850 } 851 852 public String getSid() { 853 return mHomeSystemId; 854 } 855 856 public String getNid() { 857 return mHomeNetworkId; 858 } 859 860 public boolean getCsimSpnDisplayCondition() { 861 return mCsimSpnDisplayCondition; 862 } 863 @Override 864 protected void log(String s) { 865 Rlog.d(LOG_TAG, "[RuimRecords] " + s); 866 } 867 868 @Override 869 protected void loge(String s) { 870 Rlog.e(LOG_TAG, "[RuimRecords] " + s); 871 } 872 873 @Override 874 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 875 pw.println("RuimRecords: " + this); 876 pw.println(" extends:"); 877 super.dump(fd, pw, args); 878 pw.println(" mOtaCommited=" + mOtaCommited); 879 pw.println(" mMyMobileNumber=" + mMyMobileNumber); 880 pw.println(" mMin2Min1=" + mMin2Min1); 881 pw.println(" mPrlVersion=" + mPrlVersion); 882 pw.println(" mEFpl[]=" + Arrays.toString(mEFpl)); 883 pw.println(" mEFli[]=" + Arrays.toString(mEFli)); 884 pw.println(" mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition); 885 pw.println(" mMdn=" + mMdn); 886 pw.println(" mMin=" + mMin); 887 pw.println(" mHomeSystemId=" + mHomeSystemId); 888 pw.println(" mHomeNetworkId=" + mHomeNetworkId); 889 pw.flush(); 890 } 891 892 private void setSystemProperty(String key, String val) { 893 // Update the system properties only in case NON-DSDS. 894 // TODO: Shall have a better approach! 895 if (!TelephonyManager.getDefault().isMultiSimEnabled()) { 896 SystemProperties.set(key, val); 897 } 898 } 899} 900