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 19import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM; 20 21import java.io.FileDescriptor; 22import java.io.PrintWriter; 23import java.util.ArrayList; 24import java.util.Arrays; 25import java.util.Locale; 26import android.content.Context; 27import android.os.AsyncResult; 28import android.os.Message; 29import android.os.SystemProperties; 30import android.telephony.SubscriptionManager; 31import android.telephony.Rlog; 32import android.text.TextUtils; 33import android.util.Log; 34import android.content.res.Resources; 35 36import com.android.internal.telephony.CommandsInterface; 37import com.android.internal.telephony.GsmAlphabet; 38import com.android.internal.telephony.MccTable; 39import com.android.internal.telephony.SubscriptionController; 40 41import com.android.internal.telephony.cdma.sms.UserData; 42import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; 43import com.android.internal.util.BitwiseInputStream; 44 45/** 46 * {@hide} 47 */ 48public final class RuimRecords extends IccRecords { 49 static final String LOG_TAG = "RuimRecords"; 50 51 private boolean mOtaCommited=false; 52 53 // ***** Instance Variables 54 55 private String mMyMobileNumber; 56 private String mMin2Min1; 57 58 private String mPrlVersion; 59 // From CSIM application 60 private byte[] mEFpl = null; 61 private byte[] mEFli = null; 62 boolean mCsimSpnDisplayCondition = false; 63 private String mMdn; 64 private String mMin; 65 private String mHomeSystemId; 66 private String mHomeNetworkId; 67 private String mNai; 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 mMncLength = UNINITIALIZED; 137 log("setting0 mMncLength" + mMncLength); 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 /** Returns null if RUIM is not yet ready */ 174 public String getNAI() { 175 return mNai; 176 } 177 178 @Override 179 public void setVoiceMailNumber(String alphaTag, String voiceNumber, Message onComplete){ 180 // In CDMA this is Operator/OEM dependent 181 AsyncResult.forMessage((onComplete)).exception = 182 new IccException("setVoiceMailNumber not implemented"); 183 onComplete.sendToTarget(); 184 loge("method setVoiceMailNumber is not implemented"); 185 } 186 187 /** 188 * Called by CCAT Service when REFRESH is received. 189 * @param fileChanged indicates whether any files changed 190 * @param fileList if non-null, a list of EF files that changed 191 */ 192 @Override 193 public void onRefresh(boolean fileChanged, int[] fileList) { 194 if (fileChanged) { 195 // A future optimization would be to inspect fileList and 196 // only reload those files that we care about. For now, 197 // just re-fetch all RUIM records that we cache. 198 fetchRuimRecords(); 199 } 200 } 201 202 private int adjstMinDigits (int digits) { 203 // Per C.S0005 section 2.3.1. 204 digits += 111; 205 digits = (digits % 10 == 0)?(digits - 10):digits; 206 digits = ((digits / 10) % 10 == 0)?(digits - 100):digits; 207 digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits; 208 return digits; 209 } 210 211 /** 212 * Returns the 5 or 6 digit MCC/MNC of the operator that 213 * provided the RUIM card. Returns null of RUIM is not yet ready 214 */ 215 public String getRUIMOperatorNumeric() { 216 if (mImsi == null) { 217 return null; 218 } 219 220 if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) { 221 // Length = length of MCC + length of MNC 222 // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3) 223 return mImsi.substring(0, 3 + mMncLength); 224 } 225 226 // Guess the MNC length based on the MCC if we don't 227 // have a valid value in ef[ad] 228 229 int mcc = Integer.parseInt(mImsi.substring(0,3)); 230 return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc)); 231 } 232 233 // Refer to ETSI TS 102.221 234 private class EfPlLoaded implements IccRecordLoaded { 235 @Override 236 public String getEfName() { 237 return "EF_PL"; 238 } 239 240 @Override 241 public void onRecordLoaded(AsyncResult ar) { 242 mEFpl = (byte[]) ar.result; 243 if (DBG) log("EF_PL=" + IccUtils.bytesToHexString(mEFpl)); 244 } 245 } 246 247 // Refer to C.S0065 5.2.26 248 private class EfCsimLiLoaded implements IccRecordLoaded { 249 @Override 250 public String getEfName() { 251 return "EF_CSIM_LI"; 252 } 253 254 @Override 255 public void onRecordLoaded(AsyncResult ar) { 256 mEFli = (byte[]) ar.result; 257 // convert csim efli data to iso 639 format 258 for (int i = 0; i < mEFli.length; i+=2) { 259 switch(mEFli[i+1]) { 260 case 0x01: mEFli[i] = 'e'; mEFli[i+1] = 'n';break; 261 case 0x02: mEFli[i] = 'f'; mEFli[i+1] = 'r';break; 262 case 0x03: mEFli[i] = 'e'; mEFli[i+1] = 's';break; 263 case 0x04: mEFli[i] = 'j'; mEFli[i+1] = 'a';break; 264 case 0x05: mEFli[i] = 'k'; mEFli[i+1] = 'o';break; 265 case 0x06: mEFli[i] = 'z'; mEFli[i+1] = 'h';break; 266 case 0x07: mEFli[i] = 'h'; mEFli[i+1] = 'e';break; 267 default: mEFli[i] = ' '; mEFli[i+1] = ' '; 268 } 269 } 270 271 if (DBG) log("EF_LI=" + IccUtils.bytesToHexString(mEFli)); 272 } 273 } 274 275 // Refer to C.S0065 5.2.32 276 private class EfCsimSpnLoaded implements IccRecordLoaded { 277 @Override 278 public String getEfName() { 279 return "EF_CSIM_SPN"; 280 } 281 282 @Override 283 public void onRecordLoaded(AsyncResult ar) { 284 byte[] data = (byte[]) ar.result; 285 if (DBG) log("CSIM_SPN=" + 286 IccUtils.bytesToHexString(data)); 287 288 // C.S0065 for EF_SPN decoding 289 mCsimSpnDisplayCondition = ((0x01 & data[0]) != 0); 290 291 int encoding = data[1]; 292 int language = data[2]; 293 byte[] spnData = new byte[32]; 294 int len = ((data.length - 3) < 32) ? (data.length - 3) : 32; 295 System.arraycopy(data, 3, spnData, 0, len); 296 297 int numBytes; 298 for (numBytes = 0; numBytes < spnData.length; numBytes++) { 299 if ((spnData[numBytes] & 0xFF) == 0xFF) break; 300 } 301 302 if (numBytes == 0) { 303 setServiceProviderName(""); 304 return; 305 } 306 try { 307 switch (encoding) { 308 case UserData.ENCODING_OCTET: 309 case UserData.ENCODING_LATIN: 310 setServiceProviderName(new String(spnData, 0, numBytes, "ISO-8859-1")); 311 break; 312 case UserData.ENCODING_IA5: 313 case UserData.ENCODING_GSM_7BIT_ALPHABET: 314 setServiceProviderName( 315 GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes*8)/7)); 316 break; 317 case UserData.ENCODING_7BIT_ASCII: 318 String spn = new String(spnData, 0, numBytes, "US-ASCII"); 319 // To address issues with incorrect encoding scheme 320 // programmed in some commercial CSIM cards, the decoded 321 // SPN is checked to have characters in printable ASCII 322 // range. If not, they are decoded with 323 // ENCODING_GSM_7BIT_ALPHABET scheme. 324 if (TextUtils.isPrintableAsciiOnly(spn)) { 325 setServiceProviderName(spn); 326 } else { 327 if (DBG) log("Some corruption in SPN decoding = " + spn); 328 if (DBG) log("Using ENCODING_GSM_7BIT_ALPHABET scheme..."); 329 setServiceProviderName( 330 GsmAlphabet.gsm7BitPackedToString(spnData, 0, (numBytes * 8) / 7)); 331 } 332 break; 333 case UserData.ENCODING_UNICODE_16: 334 setServiceProviderName(new String(spnData, 0, numBytes, "utf-16")); 335 break; 336 default: 337 log("SPN encoding not supported"); 338 } 339 } catch(Exception e) { 340 log("spn decode error: " + e); 341 } 342 if (DBG) log("spn=" + getServiceProviderName()); 343 if (DBG) log("spnCondition=" + mCsimSpnDisplayCondition); 344 mTelephonyManager.setSimOperatorNameForPhone( 345 mParentApp.getPhoneId(), getServiceProviderName()); 346 } 347 } 348 349 private class EfCsimMdnLoaded implements IccRecordLoaded { 350 @Override 351 public String getEfName() { 352 return "EF_CSIM_MDN"; 353 } 354 355 @Override 356 public void onRecordLoaded(AsyncResult ar) { 357 byte[] data = (byte[]) ar.result; 358 if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data)); 359 // Refer to C.S0065 5.2.35 360 int mdnDigitsNum = 0x0F & data[0]; 361 mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum); 362 if (DBG) log("CSIM MDN=" + mMdn); 363 } 364 } 365 366 private class EfCsimImsimLoaded implements IccRecordLoaded { 367 @Override 368 public String getEfName() { 369 return "EF_CSIM_IMSIM"; 370 } 371 372 @Override 373 public void onRecordLoaded(AsyncResult ar) { 374 byte[] data = (byte[]) ar.result; 375 if (VDBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data)); 376 // C.S0065 section 5.2.2 for IMSI_M encoding 377 // C.S0005 section 2.3.1 for MIN encoding in IMSI_M. 378 boolean provisioned = ((data[7] & 0x80) == 0x80); 379 380 if (provisioned) { 381 int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]); 382 int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6; 383 int digit7 = 0x0F & (data[4] >> 2); 384 if (digit7 > 0x09) digit7 = 0; 385 int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]); 386 first3digits = adjstMinDigits(first3digits); 387 second3digits = adjstMinDigits(second3digits); 388 last3digits = adjstMinDigits(last3digits); 389 390 StringBuilder builder = new StringBuilder(); 391 builder.append(String.format(Locale.US, "%03d", first3digits)); 392 builder.append(String.format(Locale.US, "%03d", second3digits)); 393 builder.append(String.format(Locale.US, "%d", digit7)); 394 builder.append(String.format(Locale.US, "%03d", last3digits)); 395 mMin = builder.toString(); 396 if (DBG) log("min present=" + mMin); 397 } else { 398 if (DBG) log("min not present"); 399 } 400 } 401 } 402 403 private class EfCsimCdmaHomeLoaded implements IccRecordLoaded { 404 @Override 405 public String getEfName() { 406 return "EF_CSIM_CDMAHOME"; 407 } 408 409 @Override 410 public void onRecordLoaded(AsyncResult ar) { 411 // Per C.S0065 section 5.2.8 412 ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result; 413 if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size()); 414 if (dataList.isEmpty()) { 415 return; 416 } 417 StringBuilder sidBuf = new StringBuilder(); 418 StringBuilder nidBuf = new StringBuilder(); 419 420 for (byte[] data : dataList) { 421 if (data.length == 5) { 422 int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF); 423 int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF); 424 sidBuf.append(sid).append(','); 425 nidBuf.append(nid).append(','); 426 } 427 } 428 // remove trailing "," 429 sidBuf.setLength(sidBuf.length()-1); 430 nidBuf.setLength(nidBuf.length()-1); 431 432 mHomeSystemId = sidBuf.toString(); 433 mHomeNetworkId = nidBuf.toString(); 434 } 435 } 436 437 private class EfCsimEprlLoaded implements IccRecordLoaded { 438 @Override 439 public String getEfName() { 440 return "EF_CSIM_EPRL"; 441 } 442 @Override 443 public void onRecordLoaded(AsyncResult ar) { 444 onGetCSimEprlDone(ar); 445 } 446 } 447 448 private void onGetCSimEprlDone(AsyncResult ar) { 449 // C.S0065 section 5.2.57 for EFeprl encoding 450 // C.S0016 section 3.5.5 for PRL format. 451 byte[] data = (byte[]) ar.result; 452 if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data)); 453 454 // Only need the first 4 bytes of record 455 if (data.length > 3) { 456 int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF); 457 mPrlVersion = Integer.toString(prlId); 458 } 459 if (DBG) log("CSIM PRL version=" + mPrlVersion); 460 } 461 462 private class EfCsimMipUppLoaded implements IccRecordLoaded { 463 @Override 464 public String getEfName() { 465 return "EF_CSIM_MIPUPP"; 466 } 467 468 boolean checkLengthLegal(int length, int expectLength) { 469 if(length < expectLength) { 470 Log.e(LOG_TAG, "CSIM MIPUPP format error, length = " + length + 471 "expected length at least =" + expectLength); 472 return false; 473 } else { 474 return true; 475 } 476 } 477 478 @Override 479 public void onRecordLoaded(AsyncResult ar) { 480 // 3GPP2 C.S0065 section 5.2.24 481 byte[] data = (byte[]) ar.result; 482 483 if(data.length < 1) { 484 Log.e(LOG_TAG,"MIPUPP read error"); 485 return; 486 } 487 488 BitwiseInputStream bitStream = new BitwiseInputStream(data); 489 try { 490 int mipUppLength = bitStream.read(8); 491 //transfer length from byte to bit 492 mipUppLength = (mipUppLength << 3); 493 494 if (!checkLengthLegal(mipUppLength, 1)) { 495 return; 496 } 497 //parse the MIPUPP body 3GPP2 C.S0016-C 3.5.8.6 498 int retryInfoInclude = bitStream.read(1); 499 mipUppLength--; 500 501 if(retryInfoInclude == 1) { 502 if (!checkLengthLegal(mipUppLength, 11)) { 503 return; 504 } 505 bitStream.skip(11); //not used now 506 //transfer length from byte to bit 507 mipUppLength -= 11; 508 } 509 510 if (!checkLengthLegal(mipUppLength, 4)) { 511 return; 512 } 513 int numNai = bitStream.read(4); 514 mipUppLength -= 4; 515 516 //start parse NAI body 517 for(int index = 0; index < numNai; index++) { 518 if (!checkLengthLegal(mipUppLength, 4)) { 519 return; 520 } 521 int naiEntryIndex = bitStream.read(4); 522 mipUppLength -= 4; 523 524 if (!checkLengthLegal(mipUppLength, 8)) { 525 return; 526 } 527 int naiLength = bitStream.read(8); 528 mipUppLength -= 8; 529 530 if(naiEntryIndex == 0) { 531 //we find the one! 532 if (!checkLengthLegal(mipUppLength, naiLength << 3)) { 533 return; 534 } 535 char naiCharArray[] = new char[naiLength]; 536 for(int index1 = 0; index1 < naiLength; index1++) { 537 naiCharArray[index1] = (char)(bitStream.read(8) & 0xFF); 538 } 539 mNai = new String(naiCharArray); 540 if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { 541 Log.v(LOG_TAG,"MIPUPP Nai = " + mNai); 542 } 543 return; //need not parsing further 544 } else { 545 //ignore this NAI body 546 if (!checkLengthLegal(mipUppLength, (naiLength << 3) + 102)) { 547 return; 548 } 549 bitStream.skip((naiLength << 3) + 101);//not used 550 int mnAaaSpiIndicator = bitStream.read(1); 551 mipUppLength -= ((naiLength << 3) + 102); 552 553 if(mnAaaSpiIndicator == 1) { 554 if (!checkLengthLegal(mipUppLength, 32)) { 555 return; 556 } 557 bitStream.skip(32); //not used 558 mipUppLength -= 32; 559 } 560 561 //MN-HA_AUTH_ALGORITHM 562 if (!checkLengthLegal(mipUppLength, 5)) { 563 return; 564 } 565 bitStream.skip(4); 566 mipUppLength -= 4; 567 int mnHaSpiIndicator = bitStream.read(1); 568 mipUppLength--; 569 570 if(mnHaSpiIndicator == 1) { 571 if (!checkLengthLegal(mipUppLength, 32)) { 572 return; 573 } 574 bitStream.skip(32); 575 mipUppLength -= 32; 576 } 577 } 578 } 579 } catch(Exception e) { 580 Log.e(LOG_TAG,"MIPUPP read Exception error!"); 581 return; 582 } 583 } 584 } 585 586 @Override 587 public void handleMessage(Message msg) { 588 AsyncResult ar; 589 590 byte data[]; 591 592 boolean isRecordLoadResponse = false; 593 594 if (mDestroyed.get()) { 595 loge("Received message " + msg + 596 "[" + msg.what + "] while being destroyed. Ignoring."); 597 return; 598 } 599 600 try { switch (msg.what) { 601 case EVENT_APP_READY: 602 onReady(); 603 break; 604 605 case EVENT_GET_DEVICE_IDENTITY_DONE: 606 log("Event EVENT_GET_DEVICE_IDENTITY_DONE Received"); 607 break; 608 609 /* IO events */ 610 case EVENT_GET_IMSI_DONE: 611 isRecordLoadResponse = true; 612 613 ar = (AsyncResult)msg.obj; 614 if (ar.exception != null) { 615 loge("Exception querying IMSI, Exception:" + ar.exception); 616 break; 617 } 618 619 mImsi = (String) ar.result; 620 621 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more 622 // than 15 (and usually 15). 623 if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) { 624 loge("invalid IMSI " + mImsi); 625 mImsi = null; 626 } 627 628 // FIXME: CSIM IMSI may not contain the MNC. 629 if (false) { 630 log("IMSI: " + mImsi.substring(0, 6) + "xxxxxxxxx"); 631 632 String operatorNumeric = getRUIMOperatorNumeric(); 633 if (operatorNumeric != null) { 634 if (operatorNumeric.length() <= 6) { 635 log("update mccmnc=" + operatorNumeric); 636 MccTable.updateMccMncConfiguration(mContext, operatorNumeric, false); 637 } 638 } 639 } else { 640 String operatorNumeric = getRUIMOperatorNumeric(); 641 log("NO update mccmnc=" + operatorNumeric); 642 } 643 644 break; 645 646 case EVENT_GET_CDMA_SUBSCRIPTION_DONE: 647 ar = (AsyncResult)msg.obj; 648 String localTemp[] = (String[])ar.result; 649 if (ar.exception != null) { 650 break; 651 } 652 653 mMyMobileNumber = localTemp[0]; 654 mMin2Min1 = localTemp[3]; 655 mPrlVersion = localTemp[4]; 656 657 log("MDN: " + mMyMobileNumber + " MIN: " + mMin2Min1); 658 659 break; 660 661 case EVENT_GET_ICCID_DONE: 662 isRecordLoadResponse = true; 663 664 ar = (AsyncResult)msg.obj; 665 data = (byte[])ar.result; 666 667 if (ar.exception != null) { 668 break; 669 } 670 671 mIccId = IccUtils.bcdToString(data, 0, data.length); 672 673 log("iccid: " + mIccId); 674 675 break; 676 677 case EVENT_UPDATE_DONE: 678 ar = (AsyncResult)msg.obj; 679 if (ar.exception != null) { 680 Rlog.i(LOG_TAG, "RuimRecords update failed", ar.exception); 681 } 682 break; 683 684 case EVENT_GET_ALL_SMS_DONE: 685 case EVENT_MARK_SMS_READ_DONE: 686 case EVENT_SMS_ON_RUIM: 687 case EVENT_GET_SMS_DONE: 688 Rlog.w(LOG_TAG, "Event not supported: " + msg.what); 689 break; 690 691 // TODO: probably EF_CST should be read instead 692 case EVENT_GET_SST_DONE: 693 log("Event EVENT_GET_SST_DONE Received"); 694 break; 695 696 case EVENT_RUIM_REFRESH: 697 isRecordLoadResponse = false; 698 ar = (AsyncResult)msg.obj; 699 if (ar.exception == null) { 700 handleRuimRefresh((IccRefreshResponse)ar.result); 701 } 702 break; 703 704 default: 705 super.handleMessage(msg); // IccRecords handles generic record load responses 706 707 }}catch (RuntimeException exc) { 708 // I don't want these exceptions to be fatal 709 Rlog.w(LOG_TAG, "Exception parsing RUIM record", exc); 710 } finally { 711 // Count up record load responses even if they are fails 712 if (isRecordLoadResponse) { 713 onRecordLoaded(); 714 } 715 } 716 } 717 718 /** 719 * Returns an array of languages we have assets for. 720 * 721 * NOTE: This array will have duplicates. If this method will be caused 722 * frequently or in a tight loop, it can be rewritten for efficiency. 723 */ 724 private static String[] getAssetLanguages(Context ctx) { 725 final String[] locales = ctx.getAssets().getLocales(); 726 final String[] localeLangs = new String[locales.length]; 727 for (int i = 0; i < locales.length; ++i) { 728 final String localeStr = locales[i]; 729 final int separator = localeStr.indexOf('-'); 730 if (separator < 0) { 731 localeLangs[i] = localeStr; 732 } else { 733 localeLangs[i] = localeStr.substring(0, separator); 734 } 735 } 736 737 return localeLangs; 738 } 739 740 @Override 741 protected void onRecordLoaded() { 742 // One record loaded successfully or failed, In either case 743 // we need to update the recordsToLoad count 744 mRecordsToLoad -= 1; 745 if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested); 746 747 if (mRecordsToLoad == 0 && mRecordsRequested == true) { 748 onAllRecordsLoaded(); 749 } else if (mRecordsToLoad < 0) { 750 loge("recordsToLoad <0, programmer error suspected"); 751 mRecordsToLoad = 0; 752 } 753 } 754 755 @Override 756 protected void onAllRecordsLoaded() { 757 if (DBG) log("record load complete"); 758 759 // Further records that can be inserted are Operator/OEM dependent 760 761 // FIXME: CSIM IMSI may not contain the MNC. 762 if (false) { 763 String operator = getRUIMOperatorNumeric(); 764 if (!TextUtils.isEmpty(operator)) { 765 log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" + 766 operator + "'"); 767 log("update icc_operator_numeric=" + operator); 768 mTelephonyManager.setSimOperatorNumericForPhone( 769 mParentApp.getPhoneId(), operator); 770 } else { 771 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping"); 772 } 773 774 if (!TextUtils.isEmpty(mImsi)) { 775 log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + mImsi) : "")); 776 mTelephonyManager.setSimCountryIsoForPhone( 777 mParentApp.getPhoneId(), 778 MccTable.countryCodeForMcc( 779 Integer.parseInt(mImsi.substring(0,3)))); 780 } else { 781 log("onAllRecordsLoaded empty imsi skipping setting mcc"); 782 } 783 } 784 785 Resources resource = Resources.getSystem(); 786 if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) { 787 setSimLanguage(mEFli, mEFpl); 788 } 789 790 mRecordsLoadedRegistrants.notifyRegistrants( 791 new AsyncResult(null, null, null)); 792 793 // TODO: The below is hacky since the SubscriptionController may not be ready at this time. 794 if (!TextUtils.isEmpty(mMdn)) { 795 int phoneId = mParentApp.getUiccCard().getPhoneId(); 796 int[] subIds = SubscriptionController.getInstance().getSubId(phoneId); 797 if (subIds != null) { 798 SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subIds[0]); 799 } else { 800 log("Cannot call setDisplayNumber: invalid subId"); 801 } 802 } 803 } 804 805 @Override 806 public void onReady() { 807 fetchRuimRecords(); 808 809 mCi.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE)); 810 } 811 812 813 private void fetchRuimRecords() { 814 mRecordsRequested = true; 815 816 if (DBG) log("fetchRuimRecords " + mRecordsToLoad); 817 818 mCi.getIMSIForApp(mParentApp.getAid(), obtainMessage(EVENT_GET_IMSI_DONE)); 819 mRecordsToLoad++; 820 821 mFh.loadEFTransparent(EF_ICCID, 822 obtainMessage(EVENT_GET_ICCID_DONE)); 823 mRecordsToLoad++; 824 825 mFh.loadEFTransparent(EF_PL, 826 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded())); 827 mRecordsToLoad++; 828 829 mFh.loadEFTransparent(EF_CSIM_LI, 830 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded())); 831 mRecordsToLoad++; 832 833 mFh.loadEFTransparent(EF_CSIM_SPN, 834 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded())); 835 mRecordsToLoad++; 836 837 mFh.loadEFLinearFixed(EF_CSIM_MDN, 1, 838 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMdnLoaded())); 839 mRecordsToLoad++; 840 841 mFh.loadEFTransparent(EF_CSIM_IMSIM, 842 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimImsimLoaded())); 843 mRecordsToLoad++; 844 845 mFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME, 846 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimCdmaHomeLoaded())); 847 mRecordsToLoad++; 848 849 // Entire PRL could be huge. We are only interested in 850 // the first 4 bytes of the record. 851 mFh.loadEFTransparent(EF_CSIM_EPRL, 4, 852 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimEprlLoaded())); 853 mRecordsToLoad++; 854 855 mFh.loadEFTransparent(EF_CSIM_MIPUPP, 856 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimMipUppLoaded())); 857 mRecordsToLoad++; 858 859 if (DBG) log("fetchRuimRecords " + mRecordsToLoad + " requested: " + mRecordsRequested); 860 // Further records that can be inserted are Operator/OEM dependent 861 } 862 863 /** 864 * {@inheritDoc} 865 * 866 * No Display rule for RUIMs yet. 867 */ 868 @Override 869 public int getDisplayRule(String plmn) { 870 // TODO together with spn 871 return 0; 872 } 873 874 @Override 875 public boolean isProvisioned() { 876 // If UICC card has CSIM app, look for MDN and MIN field 877 // to determine if the SIM is provisioned. Otherwise, 878 // consider the SIM is provisioned. (for case of ordinal 879 // USIM only UICC.) 880 // If PROPERTY_TEST_CSIM is defined, bypess provision check 881 // and consider the SIM is provisioned. 882 if (SystemProperties.getBoolean(PROPERTY_TEST_CSIM, false)) { 883 return true; 884 } 885 886 if (mParentApp == null) { 887 return false; 888 } 889 890 if (mParentApp.getType() == AppType.APPTYPE_CSIM && 891 ((mMdn == null) || (mMin == null))) { 892 return false; 893 } 894 return true; 895 } 896 897 @Override 898 public void setVoiceMessageWaiting(int line, int countWaiting) { 899 // Will be used in future to store voice mail count in UIM 900 // C.S0023-D_v1.0 does not have a file id in UIM for MWI 901 log("RuimRecords:setVoiceMessageWaiting - NOP for CDMA"); 902 } 903 904 @Override 905 public int getVoiceMessageCount() { 906 // Will be used in future to retrieve voice mail count for UIM 907 // C.S0023-D_v1.0 does not have a file id in UIM for MWI 908 log("RuimRecords:getVoiceMessageCount - NOP for CDMA"); 909 return 0; 910 } 911 912 private void handleRuimRefresh(IccRefreshResponse refreshResponse) { 913 if (refreshResponse == null) { 914 if (DBG) log("handleRuimRefresh received without input"); 915 return; 916 } 917 918 if (refreshResponse.aid != null && 919 !refreshResponse.aid.equals(mParentApp.getAid())) { 920 // This is for different app. Ignore. 921 return; 922 } 923 924 switch (refreshResponse.refreshResult) { 925 case IccRefreshResponse.REFRESH_RESULT_FILE_UPDATE: 926 if (DBG) log("handleRuimRefresh with SIM_REFRESH_FILE_UPDATED"); 927 mAdnCache.reset(); 928 fetchRuimRecords(); 929 break; 930 case IccRefreshResponse.REFRESH_RESULT_INIT: 931 if (DBG) log("handleRuimRefresh with SIM_REFRESH_INIT"); 932 // need to reload all files (that we care about) 933 onIccRefreshInit(); 934 break; 935 case IccRefreshResponse.REFRESH_RESULT_RESET: 936 // Refresh reset is handled by the UiccCard object. 937 if (DBG) log("handleRuimRefresh with SIM_REFRESH_RESET"); 938 break; 939 default: 940 // unknown refresh operation 941 if (DBG) log("handleRuimRefresh with unknown operation"); 942 break; 943 } 944 } 945 946 public String getMdn() { 947 return mMdn; 948 } 949 950 public String getMin() { 951 return mMin; 952 } 953 954 public String getSid() { 955 return mHomeSystemId; 956 } 957 958 public String getNid() { 959 return mHomeNetworkId; 960 } 961 962 public boolean getCsimSpnDisplayCondition() { 963 return mCsimSpnDisplayCondition; 964 } 965 @Override 966 protected void log(String s) { 967 Rlog.d(LOG_TAG, "[RuimRecords] " + s); 968 } 969 970 @Override 971 protected void loge(String s) { 972 Rlog.e(LOG_TAG, "[RuimRecords] " + s); 973 } 974 975 @Override 976 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 977 pw.println("RuimRecords: " + this); 978 pw.println(" extends:"); 979 super.dump(fd, pw, args); 980 pw.println(" mOtaCommited=" + mOtaCommited); 981 pw.println(" mMyMobileNumber=" + mMyMobileNumber); 982 pw.println(" mMin2Min1=" + mMin2Min1); 983 pw.println(" mPrlVersion=" + mPrlVersion); 984 pw.println(" mEFpl[]=" + Arrays.toString(mEFpl)); 985 pw.println(" mEFli[]=" + Arrays.toString(mEFli)); 986 pw.println(" mCsimSpnDisplayCondition=" + mCsimSpnDisplayCondition); 987 pw.println(" mMdn=" + mMdn); 988 pw.println(" mMin=" + mMin); 989 pw.println(" mHomeSystemId=" + mHomeSystemId); 990 pw.println(" mHomeNetworkId=" + mHomeNetworkId); 991 pw.flush(); 992 } 993} 994