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