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