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