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