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 17 package com.android.internal.telephony.uicc; 18 19 20 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; 21 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; 22 23 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; 24 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM; 25 26 import java.io.FileDescriptor; 27 import java.io.PrintWriter; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.Locale; 31 import android.content.Context; 32 import android.os.AsyncResult; 33 import android.os.Message; 34 import android.os.SystemProperties; 35 import android.telephony.TelephonyManager; 36 import android.telephony.Rlog; 37 import android.text.TextUtils; 38 39 import com.android.internal.telephony.CommandsInterface; 40 import com.android.internal.telephony.GsmAlphabet; 41 import com.android.internal.telephony.MccTable; 42 43 import com.android.internal.telephony.cdma.sms.UserData; 44 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; 45 46 47 /** 48 * {@hide} 49 */ 50 public 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 String spn = new String(spnData, 0, numBytes, "US-ASCII"); 315 // To address issues with incorrect encoding scheme 316 // programmed in some commercial CSIM cards, the decoded 317 // SPN is checked to have characters in printable ASCII 318 // range. If not, they are decoded with 319 // ENCODING_GSM_7BIT_ALPHABET scheme. 320 if (TextUtils.isPrintableAsciiOnly(spn)) { 321 setServiceProviderName(spn); 322 } else { 323 if (DBG) log("Some corruption in SPN decoding = " + spn); 324 if (DBG) log("Using ENCODING_GSM_7BIT_ALPHABET scheme..."); 325 setServiceProviderName( 326 GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes * 8) / 7)); 327 } 328 break; 329 case UserData.ENCODING_UNICODE_16: 330 setServiceProviderName(new String(spnData, 0, numBytes, "utf-16")); 331 break; 332 default: 333 log("SPN encoding not supported"); 334 } 335 } catch(Exception e) { 336 log("spn decode error: " + e); 337 } 338 if (DBG) log("spn=" + getServiceProviderName()); 339 if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition); 340 SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, getServiceProviderName()); 341 } 342 } 343 344 private class EfCsimMdnLoaded implements IccRecordLoaded { 345 @Override 346 public String getEfName() { 347 return "EF_CSIM_MDN"; 348 } 349 350 @Override 351 public void onRecordLoaded(AsyncResult ar) { 352 byte[] data = (byte[]) ar.result; 353 if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data)); 354 // Refer to C.S0065 5.2.35 355 int mdnDigitsNum = 0x0F & data[0]; 356 mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum); 357 if (DBG) log("CSIM MDN=" + mMdn); 358 } 359 } 360 361 private class EfCsimImsimLoaded implements IccRecordLoaded { 362 @Override 363 public String getEfName() { 364 return "EF_CSIM_IMSIM"; 365 } 366 367 @Override 368 public void onRecordLoaded(AsyncResult ar) { 369 byte[] data = (byte[]) ar.result; 370 if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data)); 371 // C.S0065 section 5.2.2 for IMSI_M encoding 372 // C.S0005 section 2.3.1 for MIN encoding in IMSI_M. 373 boolean provisioned = ((data[7] & 0x80) == 0x80); 374 375 if (provisioned) { 376 int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]); 377 int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6; 378 int digit7 = 0x0F & (data[4] >> 2); 379 if (digit7 > 0x09) digit7 = 0; 380 int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]); 381 first3digits = adjstMinDigits(first3digits); 382 second3digits = adjstMinDigits(second3digits); 383 last3digits = adjstMinDigits(last3digits); 384 385 StringBuilder builder = new StringBuilder(); 386 builder.append(String.format(Locale.US, "%03d", first3digits)); 387 builder.append(String.format(Locale.US, "%03d", second3digits)); 388 builder.append(String.format(Locale.US, "%d", digit7)); 389 builder.append(String.format(Locale.US, "%03d", last3digits)); 390 mMin = builder.toString(); 391 if (DBG) log("min present=" + mMin); 392 } else { 393 if (DBG) log("min not present"); 394 } 395 } 396 } 397 398 private class EfCsimCdmaHomeLoaded implements IccRecordLoaded { 399 @Override 400 public String getEfName() { 401 return "EF_CSIM_CDMAHOME"; 402 } 403 404 @Override 405 public void onRecordLoaded(AsyncResult ar) { 406 // Per C.S0065 section 5.2.8 407 ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result; 408 if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size()); 409 if (dataList.isEmpty()) { 410 return; 411 } 412 StringBuilder sidBuf = new StringBuilder(); 413 StringBuilder nidBuf = new StringBuilder(); 414 415 for (byte[] data : dataList) { 416 if (data.length == 5) { 417 int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF); 418 int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF); 419 sidBuf.append(sid).append(','); 420 nidBuf.append(nid).append(','); 421 } 422 } 423 // remove trailing "," 424 sidBuf.setLength(sidBuf.length()-1); 425 nidBuf.setLength(nidBuf.length()-1); 426 427 mHomeSystemId = sidBuf.toString(); 428 mHomeNetworkId = nidBuf.toString(); 429 } 430 } 431 432 private class EfCsimEprlLoaded implements IccRecordLoaded { 433 @Override 434 public String getEfName() { 435 return "EF_CSIM_EPRL"; 436 } 437 @Override 438 public void onRecordLoaded(AsyncResult ar) { 439 onGetCSimEprlDone(ar); 440 } 441 } 442 443 private void onGetCSimEprlDone(AsyncResult ar) { 444 // C.S0065 section 5.2.57 for EFeprl encoding 445 // C.S0016 section 3.5.5 for PRL format. 446 byte[] data = (byte[]) ar.result; 447 if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data)); 448 449 // Only need the first 4 bytes of record 450 if (data.length > 3) { 451 int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF); 452 mPrlVersion = Integer.toString(prlId); 453 } 454 if (DBG) log("CSIM PRL version=" + mPrlVersion); 455 } 456 457 @Override 458 public void handleMessage(Message msg) { 459 AsyncResult ar; 460 461 byte data[]; 462 463 boolean isRecordLoadResponse = false; 464 465 if (mDestroyed.get()) { 466 loge("Received message " + msg + 467 "[" + msg.what + "] while being destroyed. Ignoring."); 468 return; 469 } 470 471 try { switch (msg.what) { 472 case EVENT_APP_READY: 473 onReady(); 474 break; 475 476 case EVENT_GET_DEVICE_IDENTITY_DONE: 477 log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received"); 478 break; 479 480 /* IO events */ 481 case EVENT_GET_IMSI_DONE: 482 isRecordLoadResponse = true; 483 484 ar = (AsyncResult)msg.obj; 485 if (ar.exception != null) { 486 loge("Exception querying IMSI, Exception:" + ar.exception); 487 break; 488 } 489 490 mImsi = (String) ar.result; 491 492 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more 493 // than 15 (and usually 15). 494 if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) { 495 loge("invalid IMSI " + mImsi); 496 mImsi = null; 497 } 498 499 // FIXME: CSIM IMSI may not contain the MNC. 500 if (false) { 501 log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx"); 502 503 String operatorNumeric = getRUIMOperatorNumeric(); 504 if (operatorNumeric != null) { 505 if (operatorNumeric.length() <= 6) { 506 log("update mccmnc=" + operatorNumeric); 507 MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); 508 } 509 } 510 } else { 511 String operatorNumeric = getRUIMOperatorNumeric(); 512 log("NO update mccmnc=" + operatorNumeric); 513 } 514 515 break; 516 517 case EVENT_GET_CDMA_SUBSCRIPTION_DONE: 518 ar = (AsyncResult)msg.obj; 519 String localTemp[] = (String[])ar.result; 520 if (ar.exception != null) { 521 break; 522 } 523 524 mMyMobileNumber = localTemp[0]; 525 mMin2Min1 = localTemp[3]; 526 mPrlVersion = localTemp[4]; 527 528 log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1); 529 530 break; 531 532 case EVENT_GET_ICCID_DONE: 533 isRecordLoadResponse = true; 534 535 ar = (AsyncResult)msg.obj; 536 data = (byte[])ar.result; 537 538 if (ar.exception != null) { 539 break; 540 } 541 542 mIccId = IccUtils.bcdToString(data, 0, data.length); 543 544 log("iccid: " + mIccId); 545 546 break; 547 548 case EVENT_UPDATE_DONE: 549 ar = (AsyncResult)msg.obj; 550 if (ar.exception != null) { 551 Rlog.i(LOG_TAG, "RuimRecords update failed", ar.exception); 552 } 553 break; 554 555 case EVENT_GET_ALL_SMS_DONE: 556 case EVENT_MARK_SMS_READ_DONE: 557 case EVENT_SMS_ON_RUIM: 558 case EVENT_GET_SMS_DONE: 559 Rlog.w(LOG_TAG, "Event not supported: " + msg.what); 560 break; 561 562 // TODO: probably EF_CST should be read instead 563 case EVENT_GET_SST_DONE: 564 log("Event EVENT_GET_SST_DONE Received"); 565 break; 566 567 case EVENT_RUIM_REFRESH: 568 isRecordLoadResponse = false; 569 ar = (AsyncResult)msg.obj; 570 if (ar.exception == null) { 571 handleRuimRefresh((IccRefreshResponse)ar.result); 572 } 573 break; 574 575 default: 576 super.handleMessage(msg); // IccRecords handles generic record load responses 577 578 }}catch (RuntimeException exc) { 579 // I don't want these exceptions to be fatal 580 Rlog.w(LOG_TAG, "Exception parsing RUIM record", exc); 581 } finally { 582 // Count up record load responses even if they are fails 583 if (isRecordLoadResponse) { 584 onRecordLoaded(); 585 } 586 } 587 } 588 589 /** 590 * Returns an array of languages we have assets for. 591 * 592 * NOTE: This array will have duplicates. If this method will be caused 593 * frequently or in a tight loop, it can be rewritten for efficiency. 594 */ 595 private static String[] getAssetLanguages(Context ctx) { 596 final String[] locales = ctx.getAssets().getLocales(); 597 final String[] localeLangs = new String[locales.length]; 598 for (int i = 0; i < locales.length; ++i) { 599 final String localeStr = locales[i]; 600 final int separator = localeStr.indexOf('-'); 601 if (separator < 0) { 602 localeLangs[i] = localeStr; 603 } else { 604 localeLangs[i] = localeStr.substring(0, separator); 605 } 606 } 607 608 return localeLangs; 609 } 610 611 private String findBestLanguage(byte[] languages) { 612 final String[] assetLanguages = getAssetLanguages(mContext); 613 614 if ((languages == null) || (assetLanguages == null)) return null; 615 616 // Each 2-bytes consists of one language 617 for (int i = 0; (i + 1) < languages.length; i += 2) { 618 final String lang; 619 try { 620 lang = new String(languages, i, 2, "ISO-8859-1"); 621 } catch(java.io.UnsupportedEncodingException e) { 622 log("Failed to parse SIM language records"); 623 continue; 624 } 625 626 for (int j = 0; j < assetLanguages.length; j++) { 627 if (assetLanguages[j].equals(lang)) { 628 return lang; 629 } 630 } 631 } 632 633 // no match found. return null 634 return null; 635 } 636 637 private void setLocaleFromCsim() { 638 String prefLang = null; 639 // check EFli then EFpl 640 prefLang = findBestLanguage(mEFli); 641 642 if (prefLang == null) { 643 prefLang = findBestLanguage(mEFpl); 644 } 645 646 if (prefLang != null) { 647 // check country code from SIM 648 String imsi = getIMSI(); 649 String country = null; 650 if (imsi != null) { 651 country = MccTable.countryCodeForMcc( 652 Integer.parseInt(imsi.substring(0,3))); 653 } 654 log("Setting locale to " + prefLang + "_" + country); 655 MccTable.setSystemLocale(mContext, prefLang, country); 656 } else { 657 log ("No suitable CSIM selected locale"); 658 } 659 } 660 661 @Override 662 protected void onRecordLoaded() { 663 // One record loaded successfully or failed, In either case 664 // we need to update the recordsToLoad count 665 mRecordsToLoad -= 1; 666 if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested); 667 668 if (mRecordsToLoad == 0 && mRecordsRequested == true) { 669 onAllRecordsLoaded(); 670 } else if (mRecordsToLoad < 0) { 671 loge("recordsToLoad <0, programmer error suspected"); 672 mRecordsToLoad = 0; 673 } 674 } 675 676 @Override 677 protected void onAllRecordsLoaded() { 678 if (DBG) log("record load complete"); 679 680 // Further records that can be inserted are Operator/OEM dependent 681 682 // FIXME: CSIM IMSI may not contain the MNC. 683 if (false) { 684 String operator = getRUIMOperatorNumeric(); 685 if (!TextUtils.isEmpty(operator)) { 686 log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" + 687 operator + "'"); 688 log("update icc_operator_numeric=" + operator); 689 SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator); 690 } else { 691 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping"); 692 } 693 694 if (!TextUtils.isEmpty(mImsi)) { 695 log("onAllRecordsLoaded set mcc imsi=" + mImsi); 696 SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, 697 MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3)))); 698 } else { 699 log("onAllRecordsLoaded empty imsi skipping setting mcc"); 700 } 701 } 702 703 setLocaleFromCsim(); 704 mRecordsLoadedRegistrants.notifyRegistrants( 705 new AsyncResult(null, null, null)); 706 } 707 708 @Override 709 public void onReady() { 710 fetchRuimRecords(); 711 712 mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); 713 } 714 715 716 private void fetchRuimRecords() { 717 mRecordsRequested = true; 718 719 if (DBG) log("fetchRuimRecords " + mRecordsToLoad); 720 721 mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); 722 mRecordsToLoad++; 723 724 mFh.loadEFTransparent(EF_ICCID, 725 obtainMessage(EVENT_GET_ICCID_DONE)); 726 mRecordsToLoad++; 727 728 mFh.loadEFTransparent(EF_PL, 729 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded())); 730 mRecordsToLoad++; 731 732 mFh.loadEFTransparent(EF_CSIM_LI, 733 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded())); 734 mRecordsToLoad++; 735 736 mFh.loadEFTransparent(EF_CSIM_SPN, 737 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded())); 738 mRecordsToLoad++; 739 740 mFh.loadEFLinearFixed(EF_CSIM_MDN, 1, 741 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded())); 742 mRecordsToLoad++; 743 744 mFh.loadEFTransparent(EF_CSIM_IMSIM, 745 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded())); 746 mRecordsToLoad++; 747 748 mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME, 749 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded())); 750 mRecordsToLoad++; 751 752 // Entire PRL could be huge. We are only interested in 753 // the first 4 bytes of the record. 754 mFh.loadEFTransparent(EF_CSIM_EPRL, 4, 755 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded())); 756 mRecordsToLoad++; 757 758 if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested); 759 // Further records that can be inserted are Operator/OEM dependent 760 } 761 762 /** 763 * {@inheritDoc} 764 * 765 * No Display rule for RUIMs yet. 766 */ 767 @Override 768 public int getDisplayRule(String plmn) { 769 // TODO together with spn 770 return 0; 771 } 772 773 @Override 774 public boolean isProvisioned() { 775 // If UICC card has CSIM app, look for MDN and MIN field 776 // to determine if the SIM is provisioned. Otherwise, 777 // consider the SIM is provisioned. (for case of ordinal 778 // USIM only UICC.) 779 // If PROPERTY_TEST_CSIM is defined, bypess provision check 780 // and consider the SIM is provisioned. 781 if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) { 782 return true; 783 } 784 785 if (mParentApp == null) { 786 return false; 787 } 788 789 if (mParentApp.getType() == AppType.APPTYPE_CSIM && 790 ((mMdn == null) || (mMin == null))) { 791 return false; 792 } 793 return true; 794 } 795 796 @Override 797 public void setVoiceMessageWaiting(int line, int countWaiting) { 798 if (line != 1) { 799 // only profile 1 is supported 800 return; 801 } 802 803 // range check 804 if (countWaiting < 0) { 805 countWaiting = -1; 806 } else if (countWaiting > 0xff) { 807 // C.S0015-B v2, 4.5.12 808 // range: 0-99 809 countWaiting = 0xff; 810 } 811 mCountVoiceMessages = countWaiting; 812 813 mRecordsEventsRegistrants.notifyResult(EVENT_MWI); 814 } 815 816 private void handleRuimRefresh(IccRefreshResponse refreshResponse) { 817 if (refreshResponse == null) { 818 if (DBG) log("handleRuimRefresh received without input"); 819 return; 820 } 821 822 if (refreshResponse.aid != null && 823 !refreshResponse.aid.equals(mParentApp.getAid())) { 824 // This is for different app. Ignore. 825 return; 826 } 827 828 switch (refreshResponse.refreshResult) { 829 case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE: 830 if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); 831 mAdnCache.reset(); 832 fetchRuimRecords(); 833 break; 834 case IccRefreshResponse.REFRESH_RESULT_INIT: 835 if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); 836 // need to reload all files (that we care about) 837 onIccRefreshInit(); 838 break; 839 case IccRefreshResponse.REFRESH_RESULT_RESET: 840 if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); 841 if (requirePowerOffOnSimRefreshReset()) { 842 mCi.setRadioPower(false, null); 843 /* Note: no need to call setRadioPower(true). Assuming the desired 844 * radio power state is still ON (as tracked by ServiceStateTracker), 845 * ServiceStateTracker will call setRadioPower when it receives the 846 * RADIO_STATE_CHANGED notification for the power off. And if the 847 * desired power state has changed in the interim, we don't want to 848 * override it with an unconditional power on. 849 */ 850 } 851 break; 852 default: 853 // unknown refresh operation 854 if (DBG) log("handleRuimRefresh with unknown operation"); 855 break; 856 } 857 } 858 859 public String getMdn() { 860 return mMdn; 861 } 862 863 public String getMin() { 864 return mMin; 865 } 866 867 public String getSid() { 868 return mHomeSystemId; 869 } 870 871 public String getNid() { 872 return mHomeNetworkId; 873 } 874 875 public boolean getCsimSpnDisplayCondition() { 876 return mCsimSpnDisplayCondition; 877 } 878 @Override 879 protected void log(String s) { 880 Rlog.d(LOG_TAG, "[RuimRecords] " + s); 881 } 882 883 @Override 884 protected void loge(String s) { 885 Rlog.e(LOG_TAG, "[RuimRecords] " + s); 886 } 887 888 @Override 889 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 890 pw.println("RuimRecords: " + this); 891 pw.println(" extends:"); 892 super.dump(fd, pw, args); 893 pw.println(" mOtaCommited=" + mOtaCommited); 894 pw.println(" mMyMobileNumber=" + mMyMobileNumber); 895 pw.println(" mMin2Min1=" + mMin2Min1); 896 pw.println(" mPrlVersion=" + mPrlVersion); 897 pw.println(" mEFpl[]=" + Arrays.toString(mEFpl)); 898 pw.println(" mEFli[]=" + Arrays.toString(mEFli)); 899 pw.println(" mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition); 900 pw.println(" mMdn=" + mMdn); 901 pw.println(" mMin=" + mMin); 902 pw.println(" mHomeSystemId=" + mHomeSystemId); 903 pw.println(" mHomeNetworkId=" + mHomeNetworkId); 904 pw.flush(); 905 } 906 907 private void setSystemProperty(String key, String val) { 908 // Update the system properties only in case NON-DSDS. 909 // TODO: Shall have a better approach! 910 if (!TelephonyManager.getDefault().isMultiSimEnabled()) { 911 SystemProperties.set(key, val); 912 } 913 } 914 } 915