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